赞
踩
本笔记仅记录本人学习b站课程【设计模式 - 有限状态机&抽象类多态|Unity2022.2 最新教程《勇士传说》入门到进阶|4K】 及相关扩展https://www.bilibili.com/video/BV1xp4y137Xr/?share_source=copy_web&vd_source=71d8c186120345c4deff7efeb6af07cf
配合C#学习视频
ctrl+R,为变量改名字,引用的地方同步改
将资源拖拽至调色盘
创建调色盘
在层级中点加号创建方形瓦片地图
一个瓦片地图里会套多个层级的瓦片地图
每个瓦片地图可以添加并设置层级
将切割好的素材拖拽至palette中去,并保存在TileMap/palettes/forest1
palette调色盘中可以选择在具体哪个瓦片地图上绘图
人物记得锁定z轴不旋转,否则人物被撞击后可能倾倒
(添加了composite collider会自动添加rigidbody 2d),需要将rigidbody 2d设置为static,否则平台会像人物一样下落
创建配置表
add player input组件,命名为playerInputContoller
加号新建c# script
player中添加component找到刚刚新建的脚本类,自定义组件
移除【自动生成输入配置】步创建的player input组件,点击刚刚生成的playerInputContoller(下图用的是视频截图,自己的项目中叫playerInputContoller而非playerInputContol)
点击右上角生成c#类,点击apply
双击本步一开始自定义的组件
碰撞体可以让两个碰撞体不相互穿透
刚体设置,持续碰撞检测,z轴不反转
添加碰撞体,并修改碰撞体大小
添加方形碰撞器,用于站立于地面不掉下去,需要添加排除图层,防止人物可以推走敌人,敌人可以撞在一起
添加胶囊碰撞器,勾选触发器,用于伤害计算,排除敌人之间相互伤害(player中的子集多边形碰撞体也勾选了此trigger用于攻击检测,见攻击检测)
创建character类检测伤害,同时player与enemy都挂上这个组件
角色基本属性
攻击组件,设置伤害,调用character伤害方法,并且在药丸碰撞体要设置触发器
创建子集
为attack添加碰撞器
调整碰撞区域
添加属性:attack1状态
让其只在挥刀的那一帧启用碰撞体
为攻击添加attack.csharp组件并设置属性
将野猪敌人的capsule collider的图层优先级设为10,表示优先碰撞野猪的药丸碰撞体
野猪受伤无敌时间设置为1
将地面设置为ground图层
将groundLayer设置为ground图层
将groundlayer设置为ground图层
修改平台图层的碰撞检测范围
改为整个墙体都检测而非边缘最外一层,否则检测点穿过该边缘就检测不到了
点击animation与animator
将人物静态的6张图片拖至时间轴,图层会出现黄色的方块,点击时间轴右边三个点,show rate,调整帧率为9帧为一秒
将人物静态动画拉一条线到跑动动画
创建条件
速度动画临界值velocityx设置为0.1,因为设置为0计算机判断有的时候会有bug
在player目录下创建playerAnimation类并添加至player
设置跑步向静止动画的过渡
将run反向拉个线到静止
设置条件为速度小于0.1
创建混合树
双击创建的树进入
代码获取y轴速度
起跳动画
(为什么anystate不会一直进入到跳跃?因为取消勾选了can transition to self)
跳跃转落地
落地转静止动画
添加新图层,设置权重以及叠加的模式
在animation添加属性材质颜色
在0.2的位置将透明度改为0.3,0.4为1,1.0为0.5,人物闪烁效果
添加触发器参数
动画播放完了回到任意状态new state
代码中设置触发器
添加受伤事件
添加事件
选择player对象
选择playhurt方法,将其注册到onTakeDamage事件中去
在受伤时调用注册的事件
playercontroller中添加受伤反弹力属性
编写受伤被反弹效果
当在受伤状态,人物无法移动
添加受伤反弹事件
设置受伤反弹力
解决受伤后isHurt状态一直为true
编写代码,当动画结束之后,isHurt为false,则人物可以移动
添加参数
死亡为false则退出
添加死亡事件
取消死亡动画循环播放
创建参数
进入攻击状态设置
attack1->attack2
attack1->exit
当attack1播放到100%时则进入exit
所以此处的exit time表示上个动画播放到多少时且提前满足conditions,才能且一定从此通道通过,进入下个状态。
当conditions为空时,exit time其实就相当于一个条件,假射attack1->exit的箭头设置exit time为0.5,则必定形成不了三连击,因为第一个动作播放50%就满足了进入exit的通道,于是结束播放。
注:视频中使用combo无法进行连击的原因是按攻击键太快,在动画还没播放结束时,尽管在中途已经满足combo>1,但因为画面还没播放完,所以无法从此通道进入下个状态,但动画播放完时,combo已经累计3次归0,所以依然无法从此通道通过,进而进入exit,combo设置如下所示。
绑定攻击按键
在playeranimation中编写代码
playercontroller中编写代码
三段攻击都添加isAttack置false
在基础层通过anyState状态连接blueSlide时,会出现人物无法加速滑铲,isSlide状态一闪而过,在动画结束时执行OnStateExit,但测试发现该方法在第一帧就执行而非最后一帧执行,其原因是该通道设置如下
只添加了一个isSlide一个bool值,但是动画还没执行到最后一帧,在第一帧的时候就满足了isSlide=true的效果,所以从anystate立即切换到了下一个blueSlide状态,切换之后才执行前一个动画的OnStateExit中的将isSlide置false,此时将动画2执行完,但由于isSlide为false,所以加速效果完全无作用
解决办法:1、添加滑铲触发器,由于在第二次进入anystate时触发器未生效,所以不会一直切换到自己
2、取消勾选anystate到此状态通道的can transition to self,让其无法切换到自己
https://blog.csdn.net/Jaihk662/article/details/88386042
滑墙到接近地面时提前切换回了idle状态,原因是isground检测是有一个检测半径的,在人物还没到地面时,就已经检测为地面,所以isground=true,由于isground切换到true,在playercontroller中将人物材质切换回了有摩擦力,导致速度瞬间变换为0,onwall此时变为false,于是提前切换回了idle状态。
解决方案是在onwall状态时添加一个地面检测点的偏移量,在墙壁上时检测地面点偏上一点,让碰撞体与地面接触时才检测为地面。
受伤事件添加isHurt = true,但将受伤状态变为isHurt = false可以不通过动画的behavior,而通过协程进行,如下图
取消死亡动画循环播放
在某一动画帧添加敌人死亡事件
先设置project setting中的physics2d设置人物不与ignore Raycast产生碰撞
将敌人基类中生命周期改为调用状态机的方法
在此处初始化野猪敌人状态机
野猪敌人检测地面,记得加偏移量,表示当前方向前方脚下为地面检测点
1、蜗牛可能被攻击后,从skill(缩壳)状态切回patrol状态无法移动
原因:无敌时间未设置,被攻击后直接血量归0死亡
2、蜗牛发现玩家进入skill状态会重复播放
原因:没有重置losttime,在losttime<0时会切换回patrol状态,但切回去发现玩家还在前方,导致状态一直切换,同时还需要将动画的loop取消勾选
要点:1.蜗牛间隔前进,在premove动画播放时不会前进,使用下图实现(当播放premove动画时则不执行move)
2.蜗牛从背后攻击可能不掉血,因为玩家挥刀的碰撞体所在图层时player,而蜗牛触发skill状态的条件是检测到player图层,所以攻击时被检测到图层触发了无敌。解决方案如下图,将攻击图层改为default
1.蜜蜂巡逻发现玩家重写FoundPlayer()方法,使用 Physics2D.OverlapCircle(transform.position, checkDistance, attackLayer);检测玩家
2.蜜蜂用尾刺攻击玩家使用玩家用刀攻击一样的方法,在动画的某两帧激活碰撞体进行攻击,将碰撞体的位置改到尾刺。(也就是说可以尝试只播放动画的方式实现蜜蜂往前位移一下攻击玩家再回来,只要将攻击的碰撞体位置设置合理即可攻击到玩家)
3.场景中物体太多可以用下图让该物体无效
创建画布
在自动创建的eventSystem中的actions asset选择自己创建的控制器
自动切割hud文件夹的baseui
canvas下创建image
两个image分别选择血条和血条框
将血条设置为填充,横向填充,滑动条可以看到血量变化
复制出红色的,表示血量减少
将血条锚点设置在中心点,之后按alt+shift可以将血条锚点位置移动到中心点
添加组件,将图片拖拽至右侧变量
创建SO文件
在菜单中选择创建刚刚的SO文件
在character中创建血量变化事件
将刚刚创建的SO挂载到血量变化时间
创建UIManager
在UIManager中挂载代码
补充character中的扣血逻辑
修改playerStatBar代码后在OnHealthChange事件直接挂载PlayerStatBar也可以实现血量减少的效果
(右下角跨对象调用方法需要选择传递参数的对象(player(character)))
"注"中跨对象调用事件方法,个人认为是在事件中将playerStatBar对象传递过来,之后执行OnHealthChange方法。
而在"创建代码"中的发布订阅模式,实际上就是在unity生命周期初始化时将UIManager对象传递给CharacterEventSO的OnEventRaised委托,而这里的CharacterEventSO是持久化的asset文件(SO文件),在player受伤时执行character.cs的OnHealthChange事件的RaiseEvent委托(OnHealthChange.CharacterEventSO.RaiseEvent),此时该事件已在初始化时登记了CharacterEventSO的RaiseEvent委托,该委托也在初始化时登记了UIManager的OnHealthEvent方法,所以在受伤时会执行UIManager.cs的OnHealthChange方法。这里的发布订阅模式,实际上就是通过将UIManager对象传递过来,在受伤时执行UIManager的方法来实现广播给订阅者(UIManager)这个动作。
在注中,直接将playerstatbar传递进来,而使用so文件作为中间件,在character上挂载so文件,在uimanager上订阅监听so文件,实现了解耦
so文件有自己的生命周期,在游戏开始时会读取这个文件,后面使用此文件,uimanager enable时则将方法注册到中间件,后面onhealthchange时就来取这个中间件的内容。
添加插件cinemachine
新建2D camere
摄像机死区位置,未超出此范围摄像机不跟随
添加摄像机扩展组件,pixel perfect,在像素旋转时不出现像素扭曲,confiner 2d,摄像机不超出场景地图范围
新建一个empty game object,添加碰撞体,该碰撞体表示摄像机移动范围,勾选is Trigger,否则该碰撞体会触碰玩家(注:此边界一定要覆盖当前视野范围,如果小于当前视野范围则会不生效)
给bounds新增标签bounds
添加代码自动获取边界,就不用上上张图那样将bounds拖动至bounding shape 2d了。
添加空game object,并添加震动源
通过创建Camera Shake SO事件,并在CameraControl订阅该事件,并将Camera Shake SO事件添加至角色及敌人受伤事件中去实现受伤震动
mainCamera中存在声音监听组件,可以听见音源
创建AudioDefination,并挂载到攻击gameobject
创建PlayAudioEventSO文件,将AudioDefination传入的audioclip传给监听的方法播放声音
创建AudioManager监听音效事件PlayAudioEventSO
挂载到Audio Manager gameobject,并用Audio Manager gameobject中挂在的fxSource与BGMSource播放传入的音乐
添加场景打包工具addressables
打开窗口
点击create
创建完后会多一个文件夹
场景勾选打包
勾选后会自动添加到addressables group窗口
创建GameSceneSO,新建AssetReference
创建对应SO文件
选择对应场景,这样游戏开始时即可加载对应场景
创建传送点,tag选择可互动的,这样就可以在player底下子物体sign中挂载的sign方法中的OnConfirm()方法来触发场景切换方法
监听场景切换so文件
在canvas下面创建image
按住shift+alt选右下角,遮盖住整个屏幕
添加插件dotween
切换场景之后会发现人物蹬墙跳变大了,一开始以为是切换场景跳跃的数值变化了,其实是在没切场景之前蹬墙跳太小了,walljump时垂直方向的力为20却没有jump时16.5的力跳的高。
其原因主要是都在Update()函数中执行的PhysicsCheck和StateCheck执行顺序在切换场景前后有变化,先PhysicsCheck再StateCheck没有问题,但先StateCheck再PhysicsCheck,则StateCheck中使用的值是上一帧的值,如下:
onWall判断条件有三个,同时满足则为true
1、一边是墙
2、方向键朝墙那边
3、y轴速度是负值(下滑)
在起跳瞬间,由于onwall的监测点比碰撞体突出,所以在walljump时也会处于onwall,1满足
方向键一直朝墙,2满足
3是关键,在没切换场景之前,执行顺序如下图
此时,StateCheck中调用的onWall是-1帧判断的,此时速度为负值,在跳跃帧checkState在PhysicsCheck之前执行,checkState拿的速度是-1帧PhysicsCheck的速度,onwall为true,跳跃速度/1.5
切换场景之后,执行顺序如下,在跳跃帧,checkState在PhysicsCheck之后执行,此时速度为跳跃速度,正值,所以onwall为false,不影响速度
解决方案有两种:
1、加判断条件,jump时不减速
2、将 CheckState();提前至fixupdate执行,执行后才会执行inputsystem,跳跃速度便不会减小
导入.ttf字体文件并创建字体asset文件
摄像机需要设置z轴-10,否则不显示图片。这个界面需要设置摄像机,因为后面逻辑的设定,本页面寻找摄像机bounds将失效,所以需要新建一个摄像机调整位置替换原来的摄像机
加载场景时不显示血条
加载场景时禁用及启用人物操作
注:不要在加载menu场景时令人物无效,原因如下
创建一个新的sceneloadeventso命名为Scene Unload Event So
将UImanager中的事件监听换成Scene Unload Event So
在卸载场景携程执行完再执行血条有效,按原先的模式,卸载场景携程和血条控制是同步执行的,场景没卸载完就显示血条了。
也可放在场景加载完全之后
创建VoidEventSo类型的so
更改character的逻辑,新游戏重置血量,并在player、enemy上挂载
插入光源
点击add override 选择bloom(炫光)
启动后处理post processing
再调整bloom中属性,箭头会发亮(溢出炫光,调整save point rocks中的light 2d会让箭头点光源发亮)
并在代码中onEnable启用
创建Isaveable接口
创建DataDefination类为挂载的物体生成ID
在DataManager类中注册实现ISaveable接口的类,监听saveDataEvent文件,将Save方法注册到saveDataEvent,并在save方法中调用ISaveable的GetSaveData方法
创建data类,用字典存放保存的位置
在character中实现ISaveable的方法,将保存的物体写入Data类中
在需要保存的物体上挂载dataDefination用于生成ID
将sceneloader注册进存储管理
foreach会报错,原因不是下图,其为load中loadSaveData注册了SceneLoadManager的加载场景方法、character的回复血量方法,这两者是同步进行的,但是在加载场景的同时会关闭和启用人物,导致遍历saveables时,character中的loadsavedata方法无法被执行,因为关闭启用人物会执行character中的enable disable中的Register方法,读取saveables列表被临时篡改
解决:将关闭人物替换为关闭人物图片:
卸载场景之前关闭人物图片,不然人物会先移动
添加newtonsoft-json插件
设置company name、product Name
由于vector3不能序列化,所以用float序列化
将保存数据写入文件
读取保存的文件
更改character中的位置保存读取方式及sceneLoader中的位置读取
保存文件如下
创建死亡面板
添加文字
添加按钮及事件
先将面板置无效,在character ondie事件中启用面板
上一步重做,将ondie事件替换为gameOverEvent
创建onGameOverEvent so文件,并在onDie调用
添加loadDataEvent事件,增加关闭gameover面板、加载游戏、人物复活函数,并挂载至restart button
添加移动摇杆
添加按键
添加模块
创建panel
panel下创建slider
更改slider中的background的color,handle的图片
点击暴露音量为脚本参数
更改参数名
创建传递音量事件
以代码的形式添加事件,点击按钮激活或者禁用暂停界面
调换设置按钮和暂停界面顺序
UIManager通过调用pauseEvent通知AudioManager将音量传递给OnSyncVolumeEvent,再由UIManager监听,调整Slider
创建新场景
创建空物体
创建代码
addressables菜单打包模式选最后一个,第一个是在unity编辑器环境模拟运行,最后是真实打包运行(选真实打包运行可能有bug,导致获取不到音量或者敌人血量的情况)
1、手动打包
2、自动打包
sprite atlas可以将所有用到的图片打包成一张图片
将用到的文件或文件夹拖拽至右边
选择环境打包
备份项目(第一次30+min)
新建文件夹URP并右键
拖拽至project settings
点击渲染管线转换器
渲染完之后添加全局光源,场景才能正常显示,否则全黑。
若物体不亮,尝试给人物选择材质(上一步渲染管线转换器就是将所有物体材质替换为可显示材质)
可以尝试添加点光源来让人物变亮,但点光源必须要作为子物体添加在某一个物体上
https://www.bilibili.com/video/BV1ea4y1R7n5/?buvid=XYBC21EB4FCFC3A03C9195F357B2BF45C87B7&from_spmid=default-value&is_story_h5=false&mid=WGkMyEWNyK6VVuH%2F9x4CDA%3D%3D&p=1&plat_id=114&share_from=ugc&share_medium=android&share_plat=android&share_session_id=66cb9c5b-4f62-466f-bc55-f7f12c0bcdd6&share_source=COPY&share_tag=s_i&spmid=united.player-video-detail.0.0×tamp=1701916571&unique_k=KAmNlbK&up_id=455087692&vd_source=fbcd575de84e243458321a7679e9b053
将两种颜色相加,因为白色rgb是1,1,1,所以越加越接近白色,其亮度也就越大,而bloom就是让亮的像素点更亮,发出泛光
点击制作好的黑白图片
在主图片中添加次级纹理
勾选后处理
将global volume拖拽至人物场景
让场景中的全局光源的intensity小于Global Volume中的bloom中的threshold阈值,这样调整bloom中的强度intensity就不会让场景中的物体也亮起来
否则会像下面这样
创建文件夹
创建shader graph
将shader graph命名为playerGraph
右键创建材质
其自动会使用创建的shader graph
将其挂载到玩家身上
人物变成灰色
该警告表示创建的shader graph还没编写程序
双击打开shader graph
创建变量
创建sample texture 2d
连线
连线后人物出现
创建次级纹理变量,名称与之前次级纹理黑白图的纹理名称一样,让游戏场景可以根据纹理名称找到纹理
给主纹理和次级纹理添加图片
选择人物素材
将主纹理和次级纹理的sample texture相加,将结果输出到主函数
注,次级纹理只选择R,而主纹理选择RGBA,原因是选择R一个通道时,unity会自动将R转化为RGBA格式进行相加,所以当次级纹理是白色时,R=1,则转化为的RGBA是(1,1,1,1),当次级纹理是黑色时,R=0,RGBA是(0,0,0,0),这样当为黑色时,其A(透明度)就不会和主纹理中透明的部分叠加,从而让主纹理中透明的部分也变成黑色
新建color变量
添加乘法方法,黑白*任何颜色都是该颜色
保存后调整人物颜色
如下图,其原因是这些素材没有添加全黑的次级纹理,导致在乘法计算时(为空可能RGBA自动赋值为1)将其他颜色赋值到加法计算叠加到主纹理
添加全黑的次级纹理即可解决
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。