傅老师黑暗之魂复刻笔记(第一季第一部分)

问题 (答案见后)

1.
在Unity中通过一维混合树实现人物静止与跑动之间的动态切换,可以通过一维的参数混合两个动画,forward值为0时静止,1时完全走动:
在这里插入图片描述

2.
GetComponent函数全部在Awake函数里面实现。

2019.3.17
3.
所有移动rigid body的操作(也就是含rigidbody的语句),都不能在Update函数里面实现,需要在FixedUpdate()函数里面来操作。
update函数一秒走60次,fixedupdate原则上一秒走50次所以每帧的时间间隔也不一样:
前者是:Time.deltaTime后者是:Time.fixedDeltaTime

4.
以下两个函数有什么区别?
在这里插入图片描述

rigid.velocy的y分量如果是0,会复写由地心引力带来的引力作用,所以这种情况下,游戏对象会跳不起来,而且从斜坡上下来会直接走到空中再缓缓下落。而改rigid.position就不会出现这样的问题,如果非要用rigid.velocity,应该这么改:

rigid.velocity=new Vector3(movingVec.x,rigid.velocity.y,movingVec.z);

5.
在控制人物转向的时候,人物转向得太快该怎么处理?

需要用到unity的Slerp函数,与lerp函数一样是用于插值的,Slerp是sphere Lerp,球形插值,这种插值方法适用于三维向量。
详情请看

https://blog.csdn.net/andyhebear/article/details/51818534

6.
考虑到如果x、y轴范围都是正负一,当x、y轴都为1的时候,斜向速度为1.414,超过了最大速度1,所以如何把它化为一个圆形范围呢?

参见以下数学公式,可把正方形范围转换为圆形范围:
在这里插入图片描述

2019.3.17
7.
Input.GetKeyDown和Input.GetKey两个函数的区别?

GetKey函数在长按的情况下一直是True,但是GetKeyDown只在按下去的一瞬间返回True,长按也是返回一次true。

8.
unity中默认的布尔值为false。

9.
Unity中的trigger如果短暂的按两次的话会延时触发两段行为,所以该如何解决?

可以在地面上添加如下脚本,当其他trigger回到地面时,调用OnStateEnter,清除之前的trigger:

public class FSMClearSignals : StateMachineBehaviour
{
    public string[] clearSignalAtEnter;
    public string[] clearSignalAtExit;
    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    //when enters this state(ground),set every trigger to false;
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        foreach (var signal in clearSignalAtEnter)
        {
            animator.ResetTrigger(signal);
        }
    }

    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        foreach (var signal in clearSignalAtExit)
        {
            animator.ResetTrigger(signal);
        }
    }

}

10.
Unity中的animator可以通过在里面的子动画中调用message(但是这样效率不高),从而使得model上的animator上的脚本可以直接调用子动画上面的函数。
详情见下视频:
https://www.bilibili.com/video/av21513489/?p=15

2019.3.18
11.
animator里面的动画可以勾选has exit time

2019.3.19
12. Unity中的物理引擎提供了一个功能,可以在特定的位置创造一个collider,然后返回所有与其相重合(碰撞)的collider,用法如下:

     void FixedUpdate(){  
         Collider[]outputCols=Physics.OverlapCapsule(position1,position2,radius);
         if(outputCols.Length!=0)//由于返回的是一串collider,所以要判断是否有碰撞的物体
         print(1111);   //有碰撞的物体则输出
        }

如果要指定只显示特定layer的collider,应该这么写:

 void FixedUpdate()
    {
        Collider[]outputCols=Physics.OverlapCapsule(position1,position2,radius,LayerMask.GetMask("Ground"));
        if (outputCols.Length != 0)
        {
            foreach (var col in outputCols)
            {
                Debug.Log(col.name);
            }
        }
    }

2019.3.20
13. 遇到问题,跳出斜坡后,失去了原来的水平速度,是因为在之前设置了jumpexit,当退出jump状态时,关闭原来的移动向量。

2019.3.22
14. trigger类的动画事件需要记得在回到地面的时候初始化trigger的状态。

2019.3.23
15. 关于动画里面的HasExitTime:
勾选:有退出时间,即当前动画要播放完毕后才能跳向下一动画
所以我发现,当动画2和动画3都在动画1后发生,若动画2的优先级高于3,但是如果动画3的transition没有勾选hasExitTime,但是动画2的transition勾选了的话,反而会去做动画3,不做动画2。
不勾选:无退出时间,即只要满足条件即可立即跳转

测试:选择Idle到Run那条线,勾选HasExitTime,在Idle动画刚开始播放时勾选isRun,可以看到要等很久(直到Idle播放完)才会切换到Run;而不勾选HasExitTime则会立即切换

总结:一般不要勾选HasExitTime

2019.3.28
前几天去登山,处理女朋友诈骗,搞寒假作业等花费了不少时间,现在重新开始。
16. 在处理摄像机跟随角色的时候,学到了一个很巧妙的技巧,就是把摄像头上下左右四个方向的旋转,拆分成两个部分,具体层次结构如图:
在这里插入图片描述
camera放在两个handle里面,其中playerHandle负责yaw(水平)角,CameraHandle负责pitch(俯仰)角。

17. 还是接着16,出现这个问题,脚本如下:

public class CameraController : MonoBehaviour
{
    public InputPlayer inputP;
    private GameObject playerHandle;
    private GameObject cameraHandle;
    public float horizontalSpeed=20.0f;
    public float verticalSpeed = 50.0f;

    // Start is called before the first frame update
    void Awake()
    {
        cameraHandle = transform.parent.gameObject;
        playerHandle = cameraHandle.transform.parent.gameObject;
    }

    // Update is called once per frame
    void Update()
    {
        playerHandle.transform.Rotate(Vector3.up,horizontalSpeed*inputP.Cright*Time.deltaTime);
        cameraHandle.transform.Rotate(Vector3.right, -verticalSpeed * inputP.Cup * Time.deltaTime);
        cameraHandle.transform.eulerAngles = new Vector3
         (Mathf.Clamp(cameraHandle.transform.eulerAngles.x, -40.0f, 30.0f),
         0,
         0);
    }
}

但是在调整camera的时候,会发现向上移动视角会出现问题,而向下移动视角不会,视频链接如下:
https://www.bilibili.com/video/av21513489/?p=29
说是当角度跨0的时候会出现翻转,出现同位角的概念,总之这应该是与欧拉角的定义有关,具体原因remain。
总之就是,欧拉角在从正数转换为负数的时候,并没有变成负数,所以换成以下方式解决:

public class CameraController : MonoBehaviour
{
    public InputPlayer inputP;
    private GameObject playerHandle;
    private GameObject cameraHandle;
    public float horizontalSpeed=20.0f;
    public float verticalSpeed = 50.0f;
    private float tempX;

    // Start is called before the first frame update
    void Awake()
    {
        cameraHandle = transform.parent.gameObject;
        playerHandle = cameraHandle.transform.parent.gameObject;
        tempX =0f;
    }

    // Update is called once per frame
    void Update()
    {
        playerHandle.transform.Rotate(Vector3.up, horizontalSpeed * inputP.Cright * Time.deltaTime);
        //  cameraHandle.transform.Rotate(Vector3.right, -verticalSpeed * inputP.Cup * Time.deltaTime);
        tempX += -verticalSpeed * inputP.Cup * Time.deltaTime;
        tempX = Mathf.Clamp(tempX, -40.0f, 30.0f);
        cameraHandle.transform.eulerAngles = new Vector3(tempX, cameraHandle.transform.eulerAngles.y, 0);
    }
}

总之得到的结论就是不能直接对欧拉角的值进行clamp,需要使用临时值来替换。

18. Unity中,smoothDamp函数和lerp函数的区别?

首先,二者都是在Vector3下使用,(可能也可以在vector2之类下使用)。
引述:https://www.cnblogs.com/jiahuafu/p/6557915.html

Lerp更像是**线性衰减**,而SmoothDamp像是**弧形衰减**,两者都是由快而慢

其中SmoothDamp多用于相机跟随。但如果其他类型的插值,我个人觉的其实都差不多

SmoothDamp:

transform.position = Vector3.SmoothDamp(transform.position, target.position, ref velocity, smoothTime);
 
Lerp:

transform.position = Vector3.Lerp(transform.position, target.position, 0.1f);

2019.3.29
19. 关于AvatarMask
Avatar是化身的意思,(术语)指在虚拟实境中互动地呈现一个人,比如有电影Avatar(阿凡达)。
AvatarMask的作用,动画遮罩,可以指定人的哪些部位可以播放动画:
在这里插入图片描述

20. 报错:
‘ybot’ AnimationEvent ‘ClearSignalAttack’ has no receiver! Are you missing a component?
解决办法:
去找找动画事件里的event,消除event断点。

21. 关于游戏中的trigger组件,常见问题:

  • 如何用代码实现trigger,需要定义两个变量,一个为lastTrigger一个为currentTrigger,
    只有在lastTrigger为0,currentTrigger为1 的时候,启动trigger。

  • 短时间按两下trigger,会重复两次动作,解决办法:在初始动画,比如地面动画上面加脚本(挂在地面上可以方便清除其他trigger),用于在动画做完时清除掉之前的累积trigger,如下:

     using System.Collections;
     using System.Collections.Generic;
     using UnityEngine;
     
     public class FSMClearSignals : StateMachineBehaviour
     {
         public string[] clearSignalAtEnter;
         public string[] clearSignalAtExit;
    
     //when enters this state(ground),set every trigger to false;
     override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
     {
         foreach (var signal in clearSignalAtEnter)
         {
             animator.ResetTrigger(signal);
         }
     }
    
    
     // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
     override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
     {
         foreach (var signal in clearSignalAtExit)
         {
             animator.ResetTrigger(signal);
         }
     }
     }
    
  • 最后别忘了在animator里面设置SetTrigger的值。

22. 在unity的animator里面,没有api可以直接获取当前场景处于哪个动画state中,所以如果想要知道此刻位于哪一个动画状态,需要遍历所有的动画状态。
有函数可以判断特定状态是否为当前状态,实现此功能的函数为:

bool result=animator.GetCurrentAnimatorStateInfo(layerIndex).IsName(stateName);

判断第layerIndex层的名为stateName的状态,若正在此状态则为true。

23. 关于摩擦力,每一个collider上面都可以附加一个摩擦力材质,可以在Assets里面右键添加physics material。所以设置某物体的摩擦力系数,可以把生成的物理材质拖拽进去,也可以在edit–projectsettings----physics里面拖入预设材质。

24. 回顾初中知识:最大静摩擦力近似等于滑动摩擦力,实际上前者要略大一些。

25. 小操作,用于使面板整洁美观

    [Header("=====friction settings=====")]
    public PhysicMaterial frictionOne;
    public PhysicMaterial frictionZero;

效果如图:
在这里插入图片描述
稍作改动:

	[Space(10)]
    [Header("=====friction settings=====")]
    public PhysicMaterial frictionOne;
    public PhysicMaterial frictionZero;

如图:
在这里插入图片描述
Space函数里面的数字用来调整header与下面变量之间的距离。

26. 游戏bug,当角色跳起来撞到墙时,由于跳跃起来之后锁死了角色的移动,保留了速度,角色会由于摩擦力黏在墙上。
解决办法:当角色离地以后,当进入OnGroundExit时,将角色的collider的摩擦材质设为0,combined设为average,进入OnGroundEnter的时候,材质设为正常。
详情见:https://www.bilibili.com/video/av21513489/?p=36

2019.3.30
27. 动画的种类:
在这里插入图片描述

  • legacy 老的动画系统,不推荐使用。
  • Humanoid 人型骨骼动画,通过骨骼映射,去识别别的动画,应用到自己身上。
  • Generic 适用于非人型动画模型。
    参见https://blog.csdn.net/weixin_38239050/article/details/79720472、
    https://blog.csdn.net/sujun10/article/details/53339544
    2019.3.31
    28. 如何实现连击动画,使其不那么简单就能按出来:
    在击打动画中添加event:
    在这里插入图片描述
    然后在挂animator的组件下创建脚本,编写ResetTrigger的receiver:
    注意一下:如果想要提高连击按键的难度,应该把事件放在中间,而不是最后。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TriggerController : MonoBehaviour
{
    private Animator anim;
    private void Awake()
    {
        anim = GetComponent<Animator>();
    }
    public void ResetTrigger(string name)
    {
        anim.ResetTrigger(name);
    }
}

2019.4.1
29. unity的动作动画里面有一个镜像系统,如果用右手攻击的动画,勾选镜像功能之后能换成左手攻击:
在这里插入图片描述
勾选mirror之后就可以设置为换手攻击,同时mirror组件接受参数调控,不过只接受parameters面板里面的bool型变量。

30. Unity如果想设置手柄参数,设置手柄的button,有这么一个界面,其中positive button和 alt positive button 有何区别,直接设置alt positive button为何会出现一个escape?
在这里插入图片描述
alt加上之后和不加alt的设置是一样的效果,是第二按键
很有意思的是,默认加了个escape,我发现测试过程中按手柄的RB键和键盘的ESC键都可以触发结果,两个键效果是一样的。

2019.4.2
31. 公有继承:
基类中所有public成员在派生类中为public属性;
基类中所有protected成员在派生类中为protected属性;
基类中所有private成员在派生类中不可访问
注意继承是不可访问其私有成员,所以如果要访问的话要把其私有成员声明为保护或者共有类型。
protected和public成员的区别:
protected:是指给该类的子类和朋友的,所以protected的访问权限介于private和public之间.

32.
unity,console报错:
GameObject (named ‘MainCamera’) references runtime script in scene file. Fixing!
这是因为之前在GameObject上面添加了类脚本,后来把类定义成了抽象类,所以在inspector上面就看不到了,所以需要把类的abstract去掉,把脚本component移除,再改为抽象类。

33. Unity动画animator下有一条勾选选项:root motion
勾选后,可以实现动画里面的位移,但注意的是,勾选该root motion会只适用于animator挂的物体,不会移动animator挂的物体的父物体,比如playerHandle下面有model,playerHandle上面挂collider,model挂animator,这样勾选以后,collider和model会分开。
所以为了让collider跟上model,需要用函数监听model的移动量,把其赋给playerhandle

对应的函数为:

public class RootMotionController : MonoBehaviour
{
    private Animator anim;
    private void Awake()
    {
        anim = GetComponent<Animator>();
    }
    private void OnAnimatorMove()
    {
        SendMessageUpwards("OnAnimatorRootMotion",anim.deltaPosition);
    }
}

34.
如何让摄像机镜头紧跟角色,一直锁定在人物的颈椎部分。
正好之前为了X、Y轴分离,在人物的颈椎部分有一个cameraHandle,这里可以用到类似OpenGL的LookAt函数()。

35.
出现 bug:unity的camera在开始游戏的时候莫名其妙的看向天空,然后又一阵抖动看向角色。
尝试解决方案,看看camera的层级位置有没有弄错,比如说有没有用LookAt函数出现自己看自己的情况。。。

36.
使用root Motion的时候,相机抖动问题解决办法:
因为rootMotion是要移动刚体的位置,所以属于物理引擎的范畴,但是看看这个东西:
在这里插入图片描述
Animator也是一帧帧更新的,它也有animator1.DeltaTime,所以这里的更新模式应该与物理引擎一样,如下图:
在这里插入图片描述

36.
函数OnAnimatorIK:
IK的意思是 inverse kinematic 反向动力学。是根据骨骼的终节点来推算其他父节点的位置的一种方法。比如通过手的位置推算手腕、胳膊肘的骨骼的位置。”
OnAnimatorIK() is called by the Animator Component immediately before it updates its internal IK system. This callback can be used to set the positions of the IK goals and their respective weights.
根据我现在所能理解的,就是可以用来调整人物的手等部位的姿势和角度的。
参见:https://www.jianshu.com/p/7aec3699f29c
unity的animator类下有相关操作函数 ,具体的应用示例如下:

public class LeftArmAnimFix : MonoBehaviour
{
    private Animator anim;
    public Vector3 vec3;
    private void Awake()
    {
        anim = GetComponent<Animator>();
    }
    private void OnAnimatorIK()
    {
    	//取得要改变姿态的部位的transform
        Transform LeftLowerArmTransform = anim.GetBoneTransform(HumanBodyBones.LeftLowerArm);
       //设置欧拉角来旋转手臂关节
        LeftLowerArmTransform.localEulerAngles += vec3;
        //改变手臂关节的旋转参数
        anim.SetBoneLocalRotation(HumanBodyBones.LeftLowerArm, Quaternion.Euler(LeftLowerArmTransform.localEulerAngles));
    }
}

2019.4.3
37.
unity加入动画状态时,突然陷入地面的处理方法:
看看添加的动画的rig里面设置的是不是Generic,是的话需要改为Humanoid。

38.
unity获取鼠标输入的方法:用

Input.GetAxis("Mouse X");

39.
Unity如何关闭游标?
参考:https://jingyan.baidu.com/article/b2c186c80cc8b0c46ef6ff80.html
Unity中有Cursor类:

		Cursor.lockState=CursorLockMode.Confined;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.lockState = CursorLockMode.None;

Cursor有三种锁住的状态:

  • 第一种,把游标限制在当前视角之内,拉不出去,游标仍会显示。
  • 第二种是将游标固定在屏幕的中点并隐藏,按Esc重新显示。
  • 第三种相当于null,也就是不作任何处理的默认状态。

注意:关于游标的操作应该放在camera的相关程序代码里面,这是一个常规的习惯。

2019.4.4
40.
Unity报错:NullReferenceException: Object reference not set to an instance of an object
解决办法:对于自定义类,需要用这样的方式进行实例化:

public className objName=new className();

与原有的C#类不同,原有的类已经分配好了空间,但是对于自定义类需要自己分配内存空间,否则不能使用。

41.
Unity中的按键该有的几个功能:

  • 用来记住按键是否按住的按钮,相当于button功能,按下则为true,不按为false。
  • 用来记住按键触发的按钮,相当于trigger,按下的那一瞬间为true,其他时间为false。
  • 后续还有可能用到的,用来记住按键松手的按钮,松手的一瞬间为true,其他时间为false。
    2019.4.7
  • 双击功能,double trigger,很显然,这需要设置时间功能,可以在update里面随时更新当前时间,但是这样会在不同的很多脚本的update里面出现这样的东西,显得很lousy,所以可以考虑封装一个时间类,计时器。
  • 长按功能,

但是unity内不含如此复杂的功能,所以需要设置一个Button类,设置以上三个变量,方便量产button。

42.
创建计时器类来帮助实现double trigger和长按功能。

猜你喜欢

转载自blog.csdn.net/alexhu2010q/article/details/88606298