如果要开发游戏,单单使用 Painter 绘制图片会变得十分复杂。如果使用 Sprite 对象进行显示,可以简单地实现图片的位移、旋转和缩放,结合 Action 对象可以实现复杂的动画效果。最重要的是,Sprite 对象间可以存在父子关系,子 Sprite 的仿变换回受到父 Sprite 的影响。

  实现


  RectTransform 是节点对象,RectTransform 间可以通过 SetParent( ) 函数来组成结点树,使用 Visit( ) 函数来递归遍历子节点,再通过 Draw( ) 函数来执行渲染的操作,从而实现整颗渲染树的渲染。

    void RectTransform::Visit(GraphicsContext* gc)
{
if ( !bVisible ) return; this->UpdateTransform();
this->Draw(gc); for ( auto& ele : vChildList ) {
ele->Visit(gc);
}
} void RectTransform::Draw(GraphicsContext* gc)
{
// Nothing to do
}

  其中,RectTransform 有一个十分重要的属性:

Matrix4 mMatrix;

  这是一个 4 x 4 的变换矩阵,用于实现精灵的位移、旋转和缩放变换。为什么不使用 3 个不同的属性来实现精灵的位移、旋转和缩放呢?因为精灵存在父子关系,如动图所示,小的那个精灵对象的位移、旋转和缩放是建立在大精灵的变换之上。使用矩阵就可以简单地将父精灵变换传递给子精灵:

    void RectTransform::UpdateTransform()
{
mMatrix = Matrix4::makeTransform(vPosition, Vec3(vScale.x, vScale.y, 1.0), fDegrees);
if ( pParent ) {
mMatrix = pParent->mMatrix * mMatrix;
}
}

  在执行绘制操作前,会进行更新变换的操作,这样确保变换在渲染树中层层传递。

  Sprite 的实现

  Sprite 继承于 RectTransform,Sprite 只需重写 Draw( ) 函数即可。但由于还存在按钮精灵 ButtonSprite,所以将他们相同的渲染操作放到父类 RenderSprite 中,RenderSprite 只提供渲染操作:

void RenderSprite::RenderTexture(Texture2D* texture, Vec2& size, Matrix4& mat, Color& color, Anchor& anchor, GraphicsContext* gc)
{
static Vec2 blPos;
float w0 = , h0 = , w1 = size.w, h1 = size.h;
float w2 = w1 * 0.5f, h2 = h1 * 0.5f; switch ( anchor ) {
case Anchor::Anchor_TopLeft: blPos.set( w0, -h1); break;
case Anchor::Anchot_TopCenter: blPos.set(-w2, -h1); break;
case Anchor::Anchor_TopRight: blPos.set(-w1, -h1); break;
case Anchor::Anchor_CenterLeft: blPos.set( w0, -h2); break;
case Anchor::Anchor_Center: blPos.set(-w2, -h2); break;
case Anchor::Anchor_CenterRight: blPos.set(-w1, -h2); break;
case Anchor::Anchor_BottomLeft: blPos.set( w0, h0); break;
case Anchor::Anchor_BottomCenter: blPos.set(-w2, h0); break;
case Anchor::Anchor_BottomRight: blPos.set(-w1, h0); break;
} if ( pProgramEffect ) {
/* 设置顶点数据 */
vCustomTexrureVertices[].position = mat * Vec3(blPos.x + w0, blPos.y + h0, );
vCustomTexrureVertices[].position = mat * Vec3(blPos.x + w0, blPos.y + h1, );
vCustomTexrureVertices[].position = mat * Vec3(blPos.x + w1, blPos.y + h1, );
vCustomTexrureVertices[].position = mat * Vec3(blPos.x + w1, blPos.y + h0, ); vCustomTexrureVertices[].texcoord = texture->uv[];
vCustomTexrureVertices[].texcoord = texture->uv[];
vCustomTexrureVertices[].texcoord = texture->uv[];
vCustomTexrureVertices[].texcoord = texture->uv[]; vCustomTexrureVertices[].color = color;
vCustomTexrureVertices[].color = color;
vCustomTexrureVertices[].color = color;
vCustomTexrureVertices[].color = color; pProgramEffect->GetUniform("Texture0")->SetTexture(texture->textureUnique->id); gc->SetProgramEffect(pProgramEffect);
gc->SetBlendMode(SBM_AlphaTexture);
gc->GetRenderer()->AppendRenderData(vCustomTexrureVertices, , vTextureIndices, , PrimType::PT_Triangles);
}
else {
/* 设置顶点数据 */
vTextureVertices[].position = mat * Vec3(blPos.x + w0, blPos.y + h0, );
vTextureVertices[].position = mat * Vec3(blPos.x + w0, blPos.y + h1, );
vTextureVertices[].position = mat * Vec3(blPos.x + w1, blPos.y + h1, );
vTextureVertices[].position = mat * Vec3(blPos.x + w1, blPos.y + h0, ); vTextureVertices[].texcoord = texture->uv[];
vTextureVertices[].texcoord = texture->uv[];
vTextureVertices[].texcoord = texture->uv[];
vTextureVertices[].texcoord = texture->uv[]; vTextureVertices[].color = color;
vTextureVertices[].color = color;
vTextureVertices[].color = color;
vTextureVertices[].color = color; gc->SetProgram(SPT_Sprite);
gc->SetBlendMode(SBM_AlphaTexture);
gc->GetRenderer()->AppendRenderData(vTextureVertices, , vTextureIndices, , texture->textureUnique->id, PrimType::PT_Triangles);
}
}

  计算出图片 4 个顶点,设置着色程序和混合,再将渲染的顶点数据传递给 Renderer 即可。Sprite 的 Draw( ) 函数只需简单的调用 RenderTexture( ) 函数即可:

    void Sprite::Draw(GraphicsContext* gc)
{
if ( pTexture == nullptr ) return;
this->RenderTexture(pTexture, vSize, mMatrix, color, anchor, gc);
}

   ButtonSprite

  除了普通的精灵,还需要一些控件精灵。如 ButtonSprite,它能实现按钮的功能,并添加到精灵渲染树中,实现游戏中的交互操作。ButtonSprite 继承于鼠标事件监听器 MouseEventListener,从而接受鼠标事件。但 ButtonSprite 接受到一个点击事件,它先判断点击坐标是否落在 ButtonSprite 的区域内。

思路是将光标坐标变换到坐标原点,在判断坐标是否在 ButtonSprite 大小的区域内:

    bool RectTransform::AccectEvent(int x, int y)
{
Vec3 pos(x, y, ); pos = mMatrix.inverse() * pos;
float hw = vSize.w * 0.5f;
float hh = vSize.h * 0.5f; return (pos.x >= -hw && pos.x <= hw && pos.y >= -hh && pos.y <= hh);
}

  将坐标乘以变换矩阵的逆矩阵得到新的坐标,执行左边变换。如果按钮被点击了,通过信号对象传递点击信息:

   Signal<void()> SigButtonClicked;
    void ButtonSprite::MouseReleased(const MouseEvent& event)
{
if ( bMousePressed ) {
bMousePressed = false; /* 信号:按钮点击 */
SigButtonClicked();
}
}

  Signal-Slot 在前面的文章讲过,通过使用 Signal-Slot 可以实现 ButtonSprite 和其他对象的解耦。

  源码下载:Simple2D-20.rar

Simple2D-24 Sprite 渲染树的更多相关文章

  1. 【浏览器渲染原理】渲染树构建之渲染树和DOM树的关系(转载 学习中。。。)

    在DOM树构建的同时,浏览器会构建渲染树(render tree).渲染树的节点(渲染器),在Gecko中称为frame,而在webkit中称为renderer.渲染器是在文档解析和创建DOM节点后创 ...

  2. 探索未知种族之osg类生物---状态树与渲染树以及节点树之间的关系

    节点树 首先我们来看一个场景构建的实例,并通过它来了解一下“状态节点”StateGraph 和“渲染叶”RenderLeaf 所构成的状态树,“渲染台”RenderStage 和“渲染元”Render ...

  3. RenderTree渲染树

    RenderTree渲染树对类中的静态成员有很重要的关系,这个和多态是有很重要的关系,举个简单的例子,在游戏中,马里奥需要渲染,蘑菇也需要渲染,怪兽也需要渲染,其是串在一个树上的,但是不同的类型怎么将 ...

  4. [js] 渲染树构建、布局及绘制

    渲染树构建.布局及绘制

  5. 渲染树render tree

    CSSOM树和DOM树连接在一起形成一个render tree,渲染树用来计算可见元素的布局并且作为将像素渲染到屏幕上的过程的输入. DOM树和CSSOM树连接在一起形成render tree . r ...

  6. 学习opengl第一步

    有两个地址一个是学习opengl基础知识的网站, 一个是博客园大牛分享的特别好的文章. 记录一下希望向坚持做俯卧撑一样坚持下去. 学习网站:http://learnopengl-cn.readthed ...

  7. css加载是否会阻塞dom树渲染

    这里说的是头部引入css的情况 首先,我们都知道:css是由单独的下载线程异步下载的. 咱们先分析下css加载会影响什么,刚才的问题太笼统了,咱们需要细化一下. 会影响什么呢? 一个就是DOM树解析, ...

  8. 页面渲染机制(一、DOM和CSSOM树的构建)

    1.HTML的加载 HTML是一个网页的基础,下载完成后解析 2.其他静态资源加载 解析HTML时,发现其中有其他外部资源链接比如CSS.JS.图片等,会立即启用别的线程下载. 但当外部资源是JS时, ...

  9. CoreAnimation1-图层树、寄宿图以及图层几何学

    (一)图层的树状结构 Core Animation其实是一个令人误解的命名.你可能认为它只是用来做动画的,但实际上它是从一个叫做Layer Kit这么一个不怎么和动画有关的名字演变而来,所以做动画这只 ...

随机推荐

  1. .NET4.0框架退休日期逐渐临近

    微软宣布了.NET框架4.0至4.5.1版本的生命周期终结计划. 2016年1月12日之后,所有的技术支持,包含安全和非安全的更新补丁,都将会停止.开发人员和用户到时候可以选择回退到.NET 3.5 ...

  2. Cocos2d-x 2.2.3 使用NDK配置编译环境

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/wwj_748/article/details/30072379 Cocos2d-x 2.2.3 使用 ...

  3. Tensoflw.js - 01 - 安装与入门(中文注释)

    Tensoflw.js - 01 - 安装与入门(中文注释) 参考 W3Cschool 文档:https://www.w3cschool.cn/tensorflowjs/ 本文主要翻译一些英文注释,添 ...

  4. WebApi_使用技巧

    1.自动生成帮助文档  http://www.shaoqun.com/a/234059.aspx 2.Json序列化循环引用的问题http://www.mamicode.com/info-detail ...

  5. juc并发工具类之CountDownLatch闭锁

    import java.util.concurrent.CountDownLatch; /** * 闭锁: 在进行某些运算时, 只有其他所有线程的运算全部完成,当前运算才继续执行(程序流中加了一道栅栏 ...

  6. Python函数 dict()

    dict() 作用:dict() 函数用于创建一个字典.返回一个字典. 语法: class dict(**kwarg) class dict(mapping, **kwarg) class dict( ...

  7. yii2 笔记(1)

    1.js文件加载顺序问题 在视图中引用js文件的时候,使用到jquery会报错,因为自定义的js文件在jquery之前被引入了. 由于jquery,bootstrap是在AppAsset中全局引入的, ...

  8. Microsoft Dynamics CRM 2011 新建实体 需要注意的细节

    新建一个实体,需要红色框内的是否勾选的意义,可以进一步加深对CRM的理解.如图: 下面对部分的进行了自我的理解,不对的地方,还请大家指出来.互相学习. 1.CRM2011中,在活动方面加强的新特性包括 ...

  9. java 管道流PipedInputStream,PipedInputStream和随机访问文件 RandomAccessFile

    http://blog.csdn.net/zlp1992/article/details/50298195   给个链接自己去看吧.网上资料不是很多,而且自己也不想写了 RandomAccessFil ...

  10. 云-AWS-清单:清单列表

    ylbtech-云-AWS-清单:清单列表 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处:http://yl ...