游戏代码开发日志006 - 存库[使用栏]

目录

1.存库[使用栏]的吸附 – 不使用Canvas绘制UI

2.建议选择正交(Orthographic)相机还是透视(Perspective)相机?

3.存库[使用栏]的吸附 – (试图)通过设置Canvas为父物体来绘制UI(已弃用)

4.存库[使用栏]的吸附 – 获取摄像机边界坐标

5.存库[使用栏]的呼入与呼出

6.库存[使用栏][栏位]的吸附 – 用类Manager脚本实现数据分配

7. 库存[使用栏][栏位]的鼠标悬浮动效(已找到更合适的解决方式)

8.解决抖动 – 在Manager脚本中分配其它脚本的Update()优先级

9.解决遮挡 – 使用RaycastHit2D发出射线判断鼠标是否与物体产生碰撞 – 更合适的鼠标事件效果

10.效果展示


1.存库[使用栏]的吸附 – 不使用Canvas绘制UI

没想到我的第一次上色是为UI上色。

起初我不打算使用Image和Canvas来展示UI贴图,而是直接用SpriteRenderer将图片对象挂载并绘制在摄像机上。因为最初觉得多出一个很大的坐标不整洁也不美观。(后续:因为无法对于动态的分辨率来确定屏幕的宽高而不得不开始使用Canvas。)

同时,不考虑通过直接写死坐标固定UI的位置,因为未来可能会为了不同设备的长宽比而多次调整分辨率。

因为不同电脑或手机分辨率的长宽比不同,右侧的边框坐标也不同,所以我想通过获取摄像机的边框位置来固定存库[使用栏]的位置,以保持永远都靠近右侧,不会留空或不完全可见。

我们需要获取的是初始化后摄像机视野中右侧区域顶点的位置,再加上摄像机每一帧的坐标以完成吸附。

所以会用到Camera.orthographicSize(摄像机正交大小),这个值意味着摄像机视野中高度的一半,也就是原点至y轴与视野顶部交点的Size。对于宽度,乘以高宽比就可以得到了。

代码很简单,但存在一些问题。 

1.存库[使用栏]在摄像机移动的时候会出现抖动。

2.因为调用了orthographicSize,所以这种方式只适合摄像机为正交模式的情况下。

 

最初解决抖动的方法是将摄像机移动的LateUpdate()函数写进Update()里,然后把存库[使用栏]跟随移动的代码写进LateUpdate()里。

然后是透视模式不显示存库[使用栏]的问题,原以为只需调整一下透视下方的Field of View至一个合适的值就行了,但是后来没有想到透视模式不是一个合理的游戏模式。

因为当我build and run游戏后,发现存库[物品栏]并没有被正确显示在靠近右边界的位置,于是我找到了另一种更好的方式以获取摄像机的边界位置。那就是Camera.main.ViewportToWorldPoint(new Vector2( ))函数。

根据这个函数运作的原理,我们需要在正交相机和透视相机之间做出选择。

2.建议选择正交(Orthographic)相机还是透视(Perspective)相机?

遇到一个问题,Camera.main.ViewportToWorldPoint(new Vector2( ))这个函数是用于获取摄像机边界坐标的,但在某些情况下,无论填入(0,0)还是(1,1)它们都会输出MainCamera的当前坐标。

当把摄像机模式从透视相机改为正交相机时,这些参数才会正常显示。

所以说,事实上,透视相机是不存在视野边界这个概念的。如果未来想做一些需求于视野外的事件时,选择和使用正交相机的参数是必须的。

不要贪图便于设置带有立体感的布局。

3.存库[使用栏]的吸附 – (试图)通过设置Canvas为父物体来绘制UI(已弃用)

当我不使用Canvas绘制图片出现差错时,我便开始尝试使用Canvas,但发现这样似乎更加棘手。在写完这篇文章时,我依旧没能理解有些数值的分配与处理。如果你们对此有更深刻的认识,欢迎你们通过评论向我展示。

首先是,在摄像机为透视模式的情况下,把需要实现的带有SpriteRenderer和Script的组件挂在Canvas对象下之后,发现了一个奇怪的问题,那就是即使我在Update()里即使写了transform.position = new Vector3(0f, 0f, 0f);但每次游戏运行这个子组件都会得到一个非常奇怪的z轴坐标,且无法修改。

而且此时的图片显得十分小。

这个子组件的z轴坐标与Canvas的PosZ和Camera的Field of View有关,而前者与Canvas的Plane Distance和Camera的PosZ有关。

做法是调整Plane Distance至一个合适的位置以让图片呈现一个正常的大小,即使这样Z轴位置还是会很奇怪。出于父物体是RectTransform,这是一个专门设置UI组建的参数。

>疑问1:Canvas的显示为什么会对Z轴有需求?

另一个奇怪的现象是,当我从文件夹中拖出一个图片后,最初它的Scale是1,1,1,但当我将它作为Canvas的子物体拖入时它的Scale会翻数倍。起初运行游戏时这张图片可以跟随Canvas移动,但当我Reset它的坐标它就会在场景窗口中变小,而且再次运行游戏这个子物体就不再会在游戏窗口中显示。

>疑问2:SpriteRenderer图片在作为canvas的子对象时其性质会发生改变?

两张图片右侧框中的Scale均是1,1,1,但两张图片的实际大小不一样。

不断调试的过程中,对Canvas实用性的期待也逐渐消退了。

4.存库[使用栏]的吸附 – 获取摄像机边界坐标

因此,我最终选用了不使用Canvas绘制UI的方式。确定了输出方式后,接下来是使它们在需求的区域内完整执行。

第一段是输出图像,二三段通过坐标和图片大小计算位置,第四段确定和移动图像位置。别忘了第四段在Update()里也要每帧调用一次。

下图是位置获取的原理。

另外,这次虽然位移函数没有写在LateUpdate()里但并没有抖动现象。

5.存库[使用栏]的呼入与呼出

在调用呼入或呼出帧前,我们都会先根据相机位置来更新AppearX值和DisappearX值,以确定对于每次正在呼入或正在呼出都使用带MoveSpeed参数的Lerp()来控制TargetPosition.x,直到达到AppearX或DisappearX。

我们先写Incoming与Outgoing函数,bool四个状态,合理分配以保证尽量让每一帧中只有一个状态为true其它均为false。至于中间加的那个Mathf.Clamp()函数,用于防止因为摄像机在位移的时候TargetPosition没能即时跟上,我们需要将它限制在一个范围内。

Update()里我们通过默认TargetToOutgoing = true的方式每帧自行执行Outgoing()函数。不要忘记上面我们提到的,尽可能保证bool的四个状态只有一个为true。

这个IsMouseOver是新bool,我们通过鼠标移动事件来判断它。

使用OnMouseOver或OnMouseExit前别忘了需要一个BoxCollider2D组件。我们将它的size.x改成两倍,以便即使初始位置在右侧不可见时我们也可以进行呼出。

6.库存[使用栏][栏位]的吸附 – 用类Manager脚本实现数据分配

这是我们第一次同时控制6个物件,会使用到重复脚本,挂在不同对象的脚本的offsetPosition各不相同,所以我们不能在脚本内赋值,而是通过一个Manager脚本管理这数个脚本所需的offsetPosition数据。

其实Main和Normal的写法是一样的。

工整很有必要哦。

以及,记得在每个SpriteRenderer的Sorting Layer中设置各个图片显示的层级,否则这些栏位都会被背景遮挡。

7. 库存[使用栏][栏位]的鼠标悬浮动效(已找到更合适的解决方式)

我想实现的效果是当鼠标悬浮时显示的图片会顺次改变。我按照颜色对比度与明度的顺次增减画好了渐变效果。

还是与之前那样,我们使用int一个Status记录输出的图片轮换次序,利用比较时间次序轮换图片。

写一个Breathe(),实现持续的InChange()与OutChange()轮换效果。

设置HoverCollider2D = gameObject.AddComponent<BoxCollider2D>();后,Update()内部通过命令实现在判断为true时每帧调用。通过对bool值的判断调用函数往往能让代码的结构更加明晰。

别忘记在OnMouseOver()里加上鼠标悬浮时对背景碰撞的确认,否则上面的碰撞体会覆盖住背景的碰撞判断。

8.解决抖动 – 在Manager脚本中分配其它脚本的Update()优先级

图片移动抖动的原因一定与不同脚本的Update()调用顺序有关。光一个LateUpdate()或许并不够用,如果未来还会加上更多组件的话。

>方法1:将另外两个脚本的Update()改为NeedUpdate(),访问修饰符改为public,然后在Manager脚本中的Update()函数内每帧调用。请在Update()中为NeedUpdate()排序。

>方法2:使用委托(delegate)与事件(event),让每个需要执行的NeedUpdate()函数依次加入到event里,然后在Manager脚本中的Update()中每帧调用这个事件。然后在Start()中为NeedUpdate对事件的添加顺序排序。

事件在调用前总是需要加上if( != null)是为了确保在调用事件之前,已经有一个或多个方法订阅了它,从而避免空引用异常的发生。即使这里可以保证不加上也不会出错。

9.解决遮挡 – 使用RaycastHit2D发出射线判断鼠标是否与物体产生碰撞 – 更合适的鼠标事件效果

发现了一个奇怪的问题,那就是有时候有悬浮动效,也有时没有,这种情况主要出现于物品栏[使用]处于移动时。

我猜测出于Background在重新刷新绘制时遮挡了Field的碰撞体,以至于接触判断在每次Update()时不能连续生效。

现在,我们弃用前面的OnMouseOver(),将使用RaycastHit2D与一系列相关的语句来代替它的功能。

RaycastHit2D类的目的是为了创造出一个射线碰撞参数,在创建后我们可以获取到这条射线与其它物体达到的交互信息。比如用它创建的参数1,参数1.colider.gameObject就意味着射线检测碰撞到的第一个游戏对象的名称。以及,判断hit.collider.gameObject的前提是一定需要为游戏对象添加碰撞体,也就是BoxCollider2D。

用Physics2D.Raycast赋值的raycastHit2Dhit,前者的第一个参数是鼠标位置转换为的相机相对坐标,第二个参数是一个方向参数,此时我们的射线不需要方向,因此写入的是Vector2.zero。

请注意,这里的if判断条件中使用的是&&而不是&,目的是为了让我们在判断前面一个条件为不成立时不去判断后面的条件,以至于可以直接跳入else条件中,而不是因为空引用而让这条语句报错,无法跳入到else条件。

如果理解了原理,再用同样的方式做方格的鼠标悬浮效果是很简单的。

但是对于background后的field,此时对于射线来说处于更后方,即不是第一个接触到的物体,所以我们会用到数组,以及foreach遍历数组成员的方式。

Raycast返回的是一个参数,RaycastAll返回的是全部是这种参数的数组。

6个方格中另一类方格的代码同理,然后我们所需的效果就达成了。

10.效果展示

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值