主要是想记一些功能的实现思路,可能不是最优解。如果有更好的思路可以在评论交流一下
第一部分—场景
比例调整
这次选择的是2d像素风的素材,在导入的时候要调整一下 *Pixel Per Unit(每个单位所展示的像素量)*来填充画面,大部分素材绘制的时候都会使用88、16*16 、3232等比例,届时按需修改就行
(如下图所示,这一个格子就是一个单位)
素材切割
为了方便后续使用,需要用Sprite Editor把素材切开
*注:因为默认设置的原因,需要把Sprite Mode改成Multiple才能使用Sprite Editor
以人物举例,切的话可以按**格子数量(Cell Count)**切,然后这里的锚点需要改到脚底
对于场景来说就不能这样切了,需要根据格子大小(Cell Size)来把其切成小块来复用拼接
绘制
在场景中建好Tilemap直接把图扔进Tile Palette画就行,如果需要去绘制复杂的前景和背景,可以多建几层Tilemap,再通过调整图层(Sorting Layer)以达到想要的效果,数字较大的图层会覆盖较小的图层
规则瓦片(Rule Tile)
真画地图的时候不可能一点点选着画,因为还没画完人就升天了(x
于是就有了规则瓦片,配置好每张图的出现规则(通过判断上下左右有没有物体实现的),画的时候就能根据规则生成了,这里贴出地面的一部分作为演示
配好后拖到Tile Palette就可以用了
动态瓦片(Animated Tile)
这里是要做瀑布流动的效果,原理是通过图片切换来实现的,和上面一样配好图和顺序就行
第二部分—角色
刚体
人物刚体需要把Z轴冻结,不然会出现倒头就睡的情况
对于场景可以TimeMap Collider和Composite Collider搭配食用更佳,记得要把刚体类型改为Static,不改会导致平台掉落出去
玩家操作
1.按键
最新版Unity提供了新的Input System,可以直接配置按键去调用,省去了挺多麻烦
启用方法:点击顶栏的Edit-Project Settings-Player-Other Settings-Active Input Handling改为(Input System Package(New))
完成之后创建一个Input Action配置按键,搭配Player Input组件挂载到物体上就能使用了
(当然也可以让Input Action生成一个Script方便其他的Script来调用)
2.移动
实现逻辑如下,先读取按键输入的值
1 | InputDirection = inputControl.Gameplay.Move.ReadValue<Vector2>(); |
通过读取的值,在对应的方向也施加一个速度即可
1 | //涉及物理的部分要放到FixedUpdate中执行 |
然后就是人物朝向的问题,毕竟你也不希望向另一侧移动时用的是太空步罢(x
这里可以判断输入进来的值是否大于零、小于零,对这两种情况分别赋一个1、-1的值,再将其赋给transform.localscale
1 | transform.localScale = new Vector3(faceDir, 1, 1); |
3.碰撞检测
玩家:
在人物中心点绘制一个范围来检测是否有需要碰撞的图层
1 | public Vector2 bottomOffset; |
范围如果拿不准的话可以绘制一下碰撞区域
1 | private void OnDrawGizmosSelected() |
敌人:
对于敌人来说得添加两个Collider,一个用来站立于平台,另一个用来接受攻击,为避免人挤人走不动路,善用Collider的Layer Overrides(仅2022.3.20以上才有,其他版本只能另想办法了)
4.跳跃
给刚体施加一个向上瞬时的力就行
1 | //判断是否在地面,否则不能进行跳跃 |
人物动画
1.准备动画
整一个Animator Controller挂到物体身上,创建Clip即可,动画的快慢和Samples有关,可以适当调整。
2.准备变量
接着在Animator-Parameters中创建一些变量用于后面播放动画,数值的话可以在物体上挂个Script去组件里读取
这里仅展示获取到Rigidbody的速度然后赋给变量(不写全是因为太长了)
1 | rb =GetComponent<Rigidbody2D>(); |
3.设置过渡和条件
两个都搞好了,设置一下条件就能触发了。在Animator窗口中右键Clip,点”Make Transition”,再点另一个Clip就能完成过渡设置,以此类推即可。
然后就该设置条件了,点击下图的箭头,在面板中就能看到设置了,具体数值可以按需获取。
这里的Walk动画需要设置一个来回(不知道这样说对不对),Exit Time(可以提前退出动画)、Fixed Duration、Transition Duration(过渡),这三个不需要,因为这里需要立即切换动画
4.多动画切换
比如这回的跳跃动画,涉及准备、起跳、空中、下落等四个动画的切换,落地动画不包含在内是因为要单独做一个Clip。
先右键创建一个BlendTree把动画丢进去,Type、Parameters和数值看着改
Transition大概这样画,Jump接到Any State是因为任何情况都可以起跳,Land之后直接Exit,如果接到Idle的话,落地之后会顿一下很不连贯
人物属性
属性的话分为:最大生命值、当前生命值,攻击力、攻击范围、攻击频率,这两部分建议是分开写(虽说写一起也行)
人物受击
流程也很简短:
进入碰撞范围→获取对方组件中的受伤函数(无敌时间也在内执行)
1 | public void OnTriggerStay2D(Collider2D other) |
执行
1 | //受伤 |
无敌时间,这里用了一个叫计时器的方法
1 | private void Update() |
**后续补充:在这一步时,已经给敌人也添加上了刚体,一定要仔细检查要排除的图层,比如我的Box是排除了玩家和敌人层,Capsule是敌人层,勾选错误可能会导致无法触发攻击
受击动画
先新建一个层级,并在设置中调整层级的权重和混合模式(这里选的是叠加)
具体动画的话,新建一个动画片段扔到animator的新层级里,点Add Property→Material Color 手动调整rgba的值去实现受伤变色的效果,别忘了设置触发条件。
另外,由于Update代码是单次执行,所以要把受伤触发拆分出来
执行额外事件
随着开发,可能某一块功能需要执行的事件非常的多,这时可以使用Unity Events来减少代码量
1 | public UnityEvent<Transform> onTakeDamage; |
受击反弹
实现部分如下
1 | void FixedUpdate() |
写好之后把方法注册到上面写的事件里即可
然后还需要给受伤动画挂在一个Script(直接在动画的Inspector中创建就能自动生成需要的方法),用于更新isHurt变量的状态,以解决受击后人物无法移动的情况
1 | public class HurtAnimation : StateMachineBehaviour |
人物死亡
动画扔到受伤层连到Any State上,增加一个Param,然后取消Loop Time(不取消会导致动画重复播放),这块也还是用事件注册
1 | public void PlayerDead() |
人物攻击
动画部分能说的点只有切换,这里一共是做了三段的动画。
触发条件是:isAttack的值为真&触发Trigger
而切换条件则是在动画未播放完之前按下按键,即切换下一段动画,反之则直接退出
接着新建一个按键映射用来写脚本
还是老样子先注册攻击函数,这样就能实现三段攻击,不用combo作判断是因为变化非常快,导致连续攻击的时候不能进入后面的动画
1 | inputControl.Gameplay.Attack.started += PlayerAttack; |
为了解决第三段攻击执行完毕后还能接着攻击的问题,需要让动画在结束时将isAttack改为false
1 | override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) |
攻击判定
创建三个Object绑上Polygon Collider用于绑定三段攻击的判定范围
然后把碰撞体的形状调整为适合每段攻击的形状,比如:
至于怎么在合适的时间判定,可以在Animation窗口添加对应攻击的开启条件,接着打上关键帧即可
别忘了把Attack脚本挂上去,要不然没法打出伤害。
另外,还需要在碰撞体的Layer Overrides—Contact Capture Layer中只勾选Enemy层(或者在上面排除这一层也行),这样做是因为攻击范围的碰撞体可能会打到玩家的碰撞体,导致伤敌一千自损八百。
顺带把敌人的Capsule碰撞体优先级改高点,以让其优先接受碰撞。
走A部分
因为动画部分没有给对应的素材,可能导致实现效果不是很好(所以我选择保留这一部分,同时记一下禁用走A的思路)
先在角色的控制Script中,给角色移动添加相应的条件,再把对应刚体的材质加上摩擦力即可
1 | private void FixedUpdate() |
这样改完后,播放动画时,角色会往前位移一段距离,所以还需要在退出动画的脚本中添加在进入动画时要把isAttack改成true(这个就纯看个人喜好了)
1 | override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) |
刚体摩擦力
主要是为了解决不同状态下的摩擦力之类的,举个例子:比如在跳跃时摩擦力过大的话,角色容易卡到墙里。
具体实现代码如下,挺简单的
1 | public void StateCheck() |
剩下的内容就放到第二篇写了(鸽王.arw)