本笔记仅记录本人学习b站课程【设计模式 - 有限状态机&抽象类多态|Unity2022.2 最新教程《勇士传说》入门到进阶|4K】 及相关扩展https://www.bilibili.com/video/BV1xp4y137Xr/?share_source=copy_web&vd_source=71d8c186120345c4deff7efeb6af07cf
配合C#学习视频
unity常用函数
//获得子物体
transform.GetChild(0).GetComponent<Animator>();
//获得重复组件的第一个组件
GetComponents<BoxCollider2D>()[0];
//获得子物体组件(注:此方法获取到的第一个组件是自己本身的组件,第二个才是子物体!!)
GetComponentInChildren<Animator>();
//获得父物体组件
GetComponentInParent<Canvas>();
//根据名字获得当前物体的子物体的对象,注:是当前物体的子物体!
world3grid = world3.transform.Find("Grid").gameObject;
//获得所有类型为light2d的组件
lights2D = FindObjectsOfType<Light2D>();
//获得当前物体标签
this.gameObject.tag = "Untagged"
//layer是用数字判断的
gameObject.layer = 2;
//某个键按下
Keyboard.current.lKey.wasPressedThisFrame
playerInputAction.GamePlay.Confirm.started += OnConfirm;
//获得父物体
transform.parent.gameobject
//获取当前动画进度
animatorStateInfo = anim.GetCurrentAnimatorStateInfo(0);
//当动画播放完
if (animatorStateInfo.normalizedTime >= 1)
VS快捷键
ctrl+R,为变量改名字,引用的地方同步改
Unity快捷键
按住ctrl拖动瓦片地图,选择画笔,可以复制该地图,选择橡皮可以擦除
右键脚本可以看到哪里用到了这个组件,如下
调整摄像机缩放及屏幕尺寸
切割图片
打开调色盘
将资源拖拽至调色盘
创建调色盘
创建瓦片地图
在层级中点加号创建方形瓦片地图
一个瓦片地图里会套多个层级的瓦片地图
每个瓦片地图可以添加并设置层级
将切割好的素材拖拽至palette中去,并保存在TileMap/palettes/forest1
palette调色盘中可以选择在具体哪个瓦片地图上绘图
创建规则地图生成规则
创建动态地图生成规则
注:sprite editor中,绿色的线表示在canvas中不会被拉伸,如下图
添加组件
为人物添加重力以及碰撞体
人物记得锁定z轴不旋转,否则人物被撞击后可能倾倒
为平台添加碰撞体以及复合碰撞体
(添加了composite collider会自动添加rigidbody 2d),需要将rigidbody 2d设置为static,否则平台会像人物一样下落
创建背景
脚本生命周期
设置输入系统
edit-project settings设置input package
windows-package manger 安装input System
手动创建输入系统
绑定输入
创建配置表
自动生成输入配置
add player input组件,命名为playerInputContoller
代码调用输入配置
加号新建c# script
player中添加component找到刚刚新建的脚本类,自定义组件
移除【自动生成输入配置】步创建的player input组件,点击刚刚生成的playerInputContoller(下图用的是视频截图,自己的项目中叫playerInputContoller而非playerInputContol)
点击右上角生成c#类,点击apply
双击本步一开始自定义的组件
碰撞体
碰撞体可以让两个碰撞体不相互穿透
刚体设置,持续碰撞检测,z轴不反转
添加碰撞体,并修改碰撞体大小
敌人碰撞检测
添加方形碰撞器,用于站立于地面不掉下去,需要添加排除图层,防止人物可以推走敌人,敌人可以撞在一起
添加胶囊碰撞器,勾选触发器,用于伤害计算,排除敌人之间相互伤害(player中的子集多边形碰撞体也勾选了此trigger用于攻击检测,见攻击检测)
受伤检测
创建character类检测伤害,同时player与enemy都挂上这个组件
角色基本属性
攻击组件,设置伤害,调用character伤害方法,并且在药丸碰撞体要设置触发器
注:受伤事件传递无法传递参数bug
如果unityevent需要传值,那么注册的方法要选默认的
否则会变成下面这样,运行游戏时会显示参数类型无法转换
注:碰撞体勾选istrigger仍然无法调用ontriggerEnter2d方法
其原因时player没有添加rigibody2d,碰撞体必须结合rigibody2d才能调用ontriggerEnter2d。
攻击检测
创建子集
为attack添加碰撞器
调整碰撞区域
添加属性:attack1状态
让其只在挥刀的那一帧启用碰撞体
为攻击添加attack.csharp组件并设置属性
将野猪敌人的capsule collider的图层优先级设为10,表示优先碰撞野猪的药丸碰撞体
野猪受伤无敌时间设置为1
碰撞检测
将地面设置为ground图层
将groundLayer设置为ground图层
将groundlayer设置为ground图层
检测左右墙体
修改平台图层的碰撞检测范围
改为整个墙体都检测而非边缘最外一层,否则检测点穿过该边缘就检测不到了
注:碰撞体检测,OnTriggerEnter2d()等,必须要添加刚体组件才会检测到,否则无法检测!!!
创建动画控制器
创建人物静态动画
点击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产生碰撞
注:由于将敌人图层设置为2,需要将敌人伤害碰撞体的忽略图层加上ignore raycast
否则敌人死亡但还没destroy时会伤害到其他敌人
敌人状态机
将敌人基类中生命周期改为调用状态机的方法
在此处初始化野猪敌人状态机
野猪敌人检测地面,记得加偏移量,表示当前方向前方脚下为地面检测点
创建蜗牛敌人要点及问题
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.场景中物体太多可以用下图让该物体无效
UI
创建血条
创建画布
在自动创建的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
注!!跳跃bug
切换场景之后会发现人物蹬墙跳变大了,一开始以为是切换场景跳跃的数值变化了,其实是在没切场景之前蹬墙跳太小了,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
注:若物体炫光不显示,则是上一步camera没勾选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注册进存储管理
注:保存人物数据丢失bug
foreach会报错,原因不是下图,其为load中loadSaveData注册了SceneLoadManager的加载场景方法、character的回复血量方法,这两者是同步进行的,但是在加载场景的同时会关闭和启用人物,导致遍历saveables时,character中的loadsavedata方法无法被执行,因为关闭启用人物会执行character中的enable disable中的Register方法,读取saveables列表被临时篡改
解决:将关闭人物替换为关闭人物图片:
卸载场景之前关闭人物图片,不然人物会先移动
注:防止画面穿帮,添加组件
序列化保存人物
添加newtonsoft-json插件
设置company name、product Name
由于vector3不能序列化,所以用float序列化
将保存数据写入文件
读取保存的文件
更改character中的位置保存读取方式及sceneLoader中的位置读取
保存文件如下
光照
挂载在各个场景的global volume上
角色死亡界面
创建死亡面板
添加文字
添加按钮及事件
先将面板置无效,在character ondie事件中启用面板
上一步重做,将ondie事件替换为gameOverEvent
创建onGameOverEvent so文件,并在onDie调用
添加loadDataEvent事件,增加关闭gameover面板、加载游戏、人物复活函数,并挂载至restart button
注:gameover界面重复出现bug
移植移动
添加移动摇杆
添加按键
切换平台
添加模块
暂停面板
滑动条
创建panel
panel下创建slider
更改slider中的background的color,handle的图片
改变音量
点击暴露音量为脚本参数
更改参数名
创建传递音量事件
设置按钮
以代码的形式添加事件,点击按钮激活或者禁用暂停界面
注:按下设置按钮激活暂停菜单后设置按钮无法点击bug
调换设置按钮和暂停界面顺序
游戏暂停
音量滑块同步
UIManager通过调用pauseEvent通知AudioManager将音量传递给OnSyncVolumeEvent,再由UIManager监听,调整Slider
打包
创建新场景
创建空物体
创建代码
addressables菜单打包模式选最后一个,第一个是在unity编辑器环境模拟运行,最后是真实打包运行(选真实打包运行可能有bug,导致获取不到音量或者敌人血量的情况)
两种打包方式
1、手动打包
2、自动打包
打包图片素材,解决游戏过程中图片有缝隙
sprite atlas可以将所有用到的图片打包成一张图片
将用到的文件或文件夹拖拽至右边
选择环境打包
注:打包卡住,清除缓存
项目升级URP
备份项目(第一次30+min)
新建文件夹URP并右键
拖拽至project settings
点击渲染管线转换器
渲染完之后添加全局光源,场景才能正常显示,否则全黑。
若物体不亮,尝试给人物选择材质(上一步渲染管线转换器就是将所有物体材质替换为可显示材质)
注:若替换了材质之后人物是黑色的剪影,或者粒子特效变黑了
则看下global light中的目标层级是否全部勾选(sceneeffect也需要勾选,因为粒子特效的材质也是sceneeffect)
可以尝试添加点光源来让人物变亮,但点光源必须要作为子物体添加在某一个物体上
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
注:若增加副摄像机单独渲染player后,player变成黑影
其原因是摄像机所处的default层也需要勾上渲染
画面效果
制作武器炫光
原理
将两种颜色相加,因为白色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
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)将其他颜色赋值到加法计算叠加到主纹理
添加全黑的次级纹理即可解决
阴影效果
参考教程https://www.bilibili.com/video/BV1mx4y177CT?spm_id_from=333.788.videopod.sections&vd_source=fbcd575de84e243458321a7679e9b053
光照勾选shadow
添加光源组件Shadow Caster 2D
制作远程攻击
Player远程攻击
创建子弹预制体(并挂载attack.cs)、爆炸预制体
创建gun.cs(可以挂载到枪械,但这里挂载到玩家)
按下射击创建子弹预制体
子弹碰撞到敌人销毁并生成爆炸预制体
爆炸动画播放结束销毁预制体
对象池
用对象池生成子弹
销毁子弹生成爆炸特效
因为这里的子弹不是destroy而是setActive,所有不需要用用协程延迟销毁子弹,敌人可以通过传入的attack对象找到攻击对象。
注:子弹不显示bug
1、选择子弹图层为middle
2、因为一碰到其他碰撞体就销毁了,所以排除指定图层(除了敌人和地图,其他都排除)
注:爆炸特效重复播放bug
动画没有设定exit time,同时exit time要设定>1,因为代码判断播放进度>=1才销毁,如果exit time = 1,则很有可能gameobject不会被销毁,从而动画播放结束后重复播放动画。
注:子弹命中敌人后报找不到对象bug,敌人无法受击转身
注:子弹命中蜜蜂后报找不到对象bug
注:子弹命中蜗牛后蜗牛缩壳行走bug
蜗牛受伤切换到受伤动画,随后切换到缩壳,但此时蜗牛状态机没有切换到缩壳
注:对象池生成的子弹无法设置速度方向,只有撞击到敌人被销毁后才能设置速度方向
原因是入队操作用的是预制体而不是刚实例化的子弹,修改如下图
注:子弹无法追踪敌人
因为是在对象池中,所以需要在OnEnable函数中定义最大距离
敌人远程攻击
通过对象池创建远程攻击
动画播放完放回对象池
调用远程攻击
注:这里完成解耦:敌人类调用远程攻击,挂载在敌人的远程攻击类EnemyMagicAttack创建远程攻击,挂载在远程攻击预制体的AfterPoolAnimator完成对自己播放结束的销毁。
新建bringerDeathEnemy
注:敌人遇到墙壁一直转身bug及敌人在悬崖边一直转身bug
1、墙边等待结束模型转身后方向变量一直没有转身,直接进行了下次转身判定
2、悬崖边等待结束转身后检测点没有更新,isGround还是上一帧的结果,导致再次转身
注:人物墙壁检测点要加点对外延申的偏移量,否则碰撞体与墙壁接触但检测点还没检测到墙壁就无法滑墙
注:魔法攻击没有出现在玩家头顶bug
更改魔法攻击几张图片的锚点
注:魔法攻击一直循环无法销毁bug
动画结束添加的behavior文件在unity窗口中创建,并在project中将其移至对应文件夹下,如果在vs中移动,则unity中可能会找不到该文件。(一开始解决办法是在enemy.cs中读取该动画,动画播放90%后destroy)
受击动画
双击预制体,将hitAnimation放到预制体下面
弹幕伤害
安装插件Damage Numbers Pro
在hierarchy窗口添加Mesh
拖拽至预制体
添加代码
选择预制体
设置字体样式
享受弹幕吧!
残影效果
创建shadowPool
将shadowPool.cs挂载到shadowpool
创建残影预制体及代码
滑铲时创建残影
视差背景
基本原理
让背景图片跟着摄像机移动,离得近的不移动,离得远的跟着摄像机移动。
步骤
导入多张背景,设置图层、缩放
挂载ParallaxBackGround.cs脚本
设置移动速度,离得远的移动快,离得近的移动慢
完全跟着摄像机移动的底色背景直接作为摄像机的子物体
拉长背景
设置图片绘画模式tiles,调整width
选择大小调整工具可以微调
背景图片无限滚动
注:背景图片模糊,sprite editor需要更改设置
注:将图片作为地图
享受成品吧!
注:开始菜单这里为了人物移动、摄像机上移以及摄像机跟拍
1、在menu场景取消了camera
2、在SceneLoader.cs中注掉了菜单界面人物不解除控制
3、增加了空气墙
导入部署项目pakeage
1.导入项目引用的包
2.显示没有办法获取授权,则unity hub重登录
3.升级项目到urp,并按照本文上面步骤,将材质拖到project setting,否则人物是个紫色的材质
4.layer全部丢失,需要重建layer,并在physics check中引用选择
粒子特效
Unity官方手册——粒子系统https://docs.unity3d.com/cn/current/Manual/ParticleSystemModules.html
图文解释https://blog.csdn.net/w87580575/article/details/82688797
https://blog.csdn.net/asd237241291/article/details/8433534
火焰案例https://blog.csdn.net/qq_42672770/article/details/106841585
编写effectManager脚本挂载在SceneEffectManager上
制作落地和起跳的粒子特效
jumpdust在playercontroller.cs中调用
LandDust在落地动画开始时调用
WallSlide在滑铲以及滑墙时调用
敌人破碎效果粒子特效
https://www.bilibili.com/video/BV1DW4y1R77R/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=fbcd575de84e243458321a7679e9b053
敌人材质选择梯度特效的材质
粒子特效选择敌人图案
例子特效材质选择创建的叠加颜色的shadergraph的材质
原理是敌人死亡时控制梯度材质让敌人像素化,再播放例子特效。
注:粒子特效一开始就会先播放一遍bug
取消勾选awake时播放
注:dotween播放粒子特效失效bug
尾迹
尾迹粒子特效如下图
飞虫粒子特效
人物手感
土狼时间
人物起跳后土狼时间减少
蹬墙跳更灵敏
PhysicsCheck.cs是否触墙加偏移量
滑铲的速度不会随着腾空而取消
shader graph
屏幕涟漪
编写shader graph
main camera中选择rendering feature为renderer2D
在renderer2D中设置camera sorting layer texture,其会把选择的图层及其之前的图层渲染到_CameraSortingLayerTexture纹理
涟漪渲染图层选择刚刚选择的front图层之后,并将材质拖至本物体
可以获取到屏幕快照纹理了!
注:如果localscale中,本物体localscale * 父物体localscale是负值,则水波特效不显示
可以拖动水波的localscale测试
如图:
显示
不显示
遮罩
溅血到平台上
参考资料大佬的溅血教程https://blog.csdn.net/qq_36303853/article/details/140782757
血液图片选择在遮罩中显示
新建一个遮罩对象
注!遮罩比瓦片地图一个瓦片宽,和瓦片不贴和
原因是遮罩大小和图片大小一样。
遮罩的图片为256像素,设置为unity中一个单元包含256个像素
这样遮罩大小刚好就是一个单元的大小了
给规则瓦片地图应用刚刚的遮罩对象(注:只有规则瓦片地图可以添加遮罩对象,否则只能手动在场景中添加遮罩)
血液只显示在地面上!
受伤溅血粒子特效勾选send collision message
在该粒子特效上挂载代码创建血液预制体
在血液预制体上挂载销毁脚本
杀戮吧!
注:血与平台产生碰撞才会触发ParticleCollisionEvent,必须给遮罩添加碰撞体
如果不是用瓦片地图而是用的sprite mask,则必须要给mask添加碰撞体,否则粒子特效将会穿过该碰撞体。
怪物寻路
创建NavMesh Surface物体
给瓦片地图的platform添加navigation modifier,并设置not walkable
对于地面行走的敌人,添加noway瓦片地图,给除了地面的其他区域涂满瓦片,并如上图标记not walkable
对于有台阶的瓦片地图,地面敌人需要重力设置小一点,否则无法上台阶
设置半径为0.005,点击rotate后点击bake
给敌人挂载NavItem.cs以及nav mesh agent组件
编写NavItem.cs
编写状态机
注:敌人到达target后鬼畜转身bug
注:添加隔墙无法发现玩家逻辑后,玩家贴地行走,无法发现玩家bug
建议使用raycast而非上图的boxcast
如果checksize为一个长方形,那么会碰到地面,则检测为敌人和player之间有墙
注:off mesh link无法生效bug
该组件只能运用于humanoid agent type,运用在其他agent type不生效。
注:SetDestination报错bug
报错信息如下
“SetDestination” can only be called on an active agent that has been placed on a NavMesh.
要把对应的navmesh surface打开(比如蝙蝠是fly)
注:bloodking在多跳台上丢失玩家目标,而一直在跳跃动画无法结束,且玩家到其身边也无法发现玩家进入chase行为bug
1、无法发现玩家原因:
发现玩家脚本中,非跳跃帧才能发现玩家,而boss在跳跃帧出不来,则无法发现。
2、跳跃动画无法结束bug
BloodKingPatrol.cs中在跳跃帧就无法进入判断,关闭代理,结束跳跃帧,形成死锁
解决方法:加入如下代码
注:敌人使用寻路代理后,就算设置了重力,也偏离地面行走bug
如图
解决办法:baseoffset设置正值1+即可,暂不知原因,同时baseoffset会影响怪物穿越offmeshlink的高度,当其为1时,敌人穿越offmeshlink会瞬间从距离地面高度为1的点经过offmeshlink
注:怪物通过nav mesh agent行走,离地有小间隙
此间隙并非是上一个问题产生,其原因是sprite editor中pivot没有在脚底,与脚底有几个像素,agent是根据当前物体锚点进行导航的
注:radius参数会影响瞬移距离,当怪物靠近offmeshlink点时,如果radius很大,怪物会直接瞬移至link
注:多跳台连接时,怪物无法导航至玩家,在跳台间来回横跳
其原因时offmeshlink之间设置过近,在一个单元内
尽量将link设置远一些,不在一个单元,如下图
boss地面跳跃
添加跳跃点
会有两个圆圈表示可直达
添加跳跃曲线,表示boss在空中的速度变化
在offmeshlink(处于navmeshlink中,此值也为true)时,通过dotween做跳跃
行为树behavior designer
执行顺序
参考文章https://www.jianshu.com/p/998f665cb616
同一sequence下执行顺序
同一个sequence下,节点顺序执行,从左往右执行,如下图,当判断条件在最左边时,判断条件不满足则不执行右边的节点
当判断条件在右边时,不经过判断条件即可执行左边的节点
selector下不同sequence下执行顺序
行为树从左往右执行,如图,两个sequence都勾选了lower priority
lower priority:当运行到后续节点时,本节点的判断生效了的话则打断当前执行顺序,返回本节点执行!
但如果两个判断条件都满足,则会执行左边的队列,因为selector下第一层节点是if else if关系,当左边队列满足了,即使右边的check也满足,也先执行左边的sequence。
人物切换背景图层场景
新增virtual camera,让画面变大
当按下l键,激活此摄像机
激活后,cinemachinebarin会自动切换到这个virtual camera,设置blend类型即可控制缩放速度
切换背景图层1.0,背景放大,人物不变
新增副摄像机,只渲染人物和default图层(default图层一定要勾选,因为摄像机也处于这个图层,不勾选,渲染会出问题,比如黑影)
副摄像机勾选了透视投影,并且culling mask也勾选player,会发现副摄像机和主摄像机渲染出了不同大小的人物:
此时,保持副摄像机激活状态去Virtual Camera(Scene Trans)中可以单独控制此摄像机player的缩放。取消激活副摄像机后,此Virtual Camera(Scene Trans)又可以控制主摄像机的缩放,借此可以做到人物与背景不同缩放倍数的效果。这个93代表透视投影的区域大小
最后在代码控制l键按下,主摄像机渲染图层改变,player交给副摄像机,同时开关virtual camera即可完成背景图层切换的摄像机缩放,代码见SceneTrans.cs
切换背景图层2.0
主摄像机一开始渲染所有图层
副摄像机渲染必要图层(本场景图层)
人物摄像机只拍人物和灯光,因为人物在背景切换中大小不变
场景转换中的主摄像机只渲染临时图层(即将淡入的场景目前处于tmp层,由主摄渲染)以及背景,这样场景转换时临时网格图层将由主摄像机渲染,其他图层将由副摄像机渲染,可以实现一个渐入,另一个渐出同时进行
在SceneTrans.cs代码中会记录瓦片地图的图层及下一世界的灯光强度信息
用于切换完成后的恢复
当场景切换时,调整摄像机缩放及各个世界的瓦片地图透明度
摄像机缩放通过本背景图层与下个世界的背景图层position.z的值确认,下个世界深度较深,则渐进,否则远离。
效果如下: