跳轉至

绘制方法入门

在之前的教程中,我们已经学习了如何通过hook去修改游戏的运行逻辑。但只有功能而没有画面的mod是不完整的,那么我们如何添加自己的视觉效果呢?

这时候就需要我们的IDrawble接口了,所有继承于IDrawable的物体在添加到房间后都会被绘制。

现在我们创建一个类型CoolEffect继承UpdatableAndDeletable和IDrawable接口。

创建CoolEffect

并且用以往的方式hook并在Player.Update里创建这个物体

Player创建物体

可以看到IDrawble提供了三个函数,其中参数类型也都比较陌生,但是不要怕我们一一来解释。


一.创建贴图

首先是InitiateSprites 其中Initiate的意思是初始化,那后面的sprites是什么呢? 在雨世界中,画面的显示是依靠sprite(精灵)显示的。每一个实际显示在屏幕里的物体都是由多个sprite构成的,比如石头是单个sprite而玩家则是12+个。

sprite显示

那这个函数的意思就很明朗了,对物体的sptire进行初始化。先看第一个参数SpriteLeaser,它代表着这个物体对应的sprites组,其实它还有很多其他的函数和功能,不过我们暂时只使用sprites。我们目前只需要一个贴图,所以sprites创建成一个1长度的数组,并且给数组的零位上创建一个FSprite。

sprite创建

FSprite的构造函数里需要填写你所要显示贴图的名称,关于如何导入自定义贴图之后会讲,暂时使用Futile_White,这是一个rw自带的正方形的白色贴图。接下来我们可以设置一下sprite的一些参数,比如位置x,y旋转角度rotation,亦或是颜色color。

sprite属性

到现在我们已经成功在sLeaser里创建了一个贴图,但是如果你运行程序会发现,图片并不会显示在游戏里。这时候就需要第二个函数——AddtoContainer出场了。


二.把贴图显示出来

首先我们先把AddToContainer写在InitiateSprites函数的结尾处,像这样。

AddToContainer

AddToContainer,直接翻译的话就时添加到容器,但是我们可以换个说法理解—把sprite添加到图层。Rw中有着图层前后之分,比如蛞蝓猫会被实体砖块遮挡,但还可以遮挡后面的背景这就是因为三者处在不同的图层上。所以我们的sprite必须要分配到对应的图层里才能进行显示。而第三个参数FContainer就是我们要添加到的图层。

那么我们需要做的就是把sprites[0]添加到我们的第三个参数newContatiner里,调用AddChild可以很轻松的做到这点。

newContatiner.AddChild(sLeaser.sprites[0]);

但是有人可能会发现一些问题,在InitiateSprites里我们传的newContainer是null,并不能直接调用。那么就需要我们自己去获取一个图层了。

如何获取一个显示图层呢,我们就需要用到第二个参数RoomCamera了。这个很好理解,和名称一样拍摄房间内的所有信息,所有房间内的显示都是它最后进行显示处理。

这是一个复杂的类,我们目前只需要用到ReturnFContainer这个函数,这个函数可以根据你给定的图层名字返回对应的sprite容器,这里我们使用Midground层。这里也给出了常用的图层和对应的用途。

图层名 常见用途
Background 游戏的背景
Midground 游戏中猫猫,其他生物常见的图层
Items 游戏中物体的图层,比Midground高一层
Foreground 游戏地图中的前景部分
HUD 游戏界面所在的层,可以放一些不应该被遮挡的特效

现在在原有的AddToContainer前面加上判断,如果newContainer为空,那么从rCam获取一个Midground的图层。

获取图层

现在再次编译运行,你就可以看到图片显示在屏幕的固定位置。

效果


三、让贴图动起来

贴图光显示出来当然是不够的,想让贴图真正动起来需要使用第三个函数——DrawSprites。这个函数相当于针对绘制的Update。

这里我们要让这个物体在横向上以三角函数的方式往返运动,为了能够把运行的时间作为Mathf.Sin的参数,我们需要在Update用以往的知识写一个counter。

Counter

还记得我们在InitiateSprites提到的sprites属性嘛,现在我们需要用到x这个属性,它代表sprite的横向位置。在原有的500后面添加。

sin代码

再次运行,我们的效果终于动起来了。(恭喜)

gif效果,但是我不会录制gif

不过现在还不是很完美,当摄像机调整位置时贴图的位置不会变化,这时候就需要第四个参数camPos上场了。

在rw中实际的显示位置和游戏内的位置是如下图的关系

camPos和pos和DrawPos

所以我们可以在原来的语句后面加上 – camPos。再进游戏就会发现效果可以正确的跟随摄像头了。(有些时候会被遮挡,那如果我想让它的位置以玩家位置为中心该怎么办呢,大家可以思考一下并自己试着改一下)

camPos代码

看起来一切都很完美?别急,我们还有最后一点要改动。如果你开启开发者工具并按下A就会发现我们创建的CoolEffect一卡一卡的而猫猫的动作缺依然顺滑,这就不够cool了。

发生这个现象的原因是什么呢? 在RW中负责逻辑的Update和负责显示的DrawSprites并不是完全同步的,实际上往往DrawSprites调用了多次Update才能调用一次。

那有人就会想“那我把counter放到DrawSprites计数不就好了嘛。”这个能确实解决顺滑问题,但是DrawSprite的调用速率是不固定的,可能电脑卡一点运动就慢一点,电脑流畅一点运动就快一点。

这显然不是我们所希望的。想解决流畅度的问题,就要用到第三个参数timeStacker了。Update、Drawsprites还有TimeStacker的关系可以用下图表示。

timeStacker图,但是我画图不好所以现在没有

timeStacker可以表示从上一次Update触发到下一次触发的进度。巧合的是,每次Update后counter正好+1,那么我们便可以使用把这个timeStacker加到counter上,这样我们的动画就平滑起来了。

timeStacker使用


练习

1.让CoolEffect根据时间进行转动

2.让CoolEffect跟随创建它的玩家位置,并且可以顺滑的移动

評論