欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity官方教程Ruby大冒险的自学笔记

程序员文章站 2022-03-31 20:44:31
...

Unity官方教程Ruby大冒险的自学笔记

一、
//正确例子:

void Update()
    {
            //获取运动矢量
            moveX = Input.GetAxisRaw("Horizontal");
            moveY = Input.GetAxisRaw("Vertical");

        Vector2 position = rbody.position;
        position.x += moveX * speed * Time.fixedDeltaTime;
        position.y += moveY * speed * Time.fixedDeltaTime;

        //用刚体移动可以消除画面抖动
        rbody.MovePosition(position);
    }

//错误例子:

    Vector2 position = transform.position;
    position.x += moveX * speed * Time.deltaTime;
    position.y += moveY * speed * Time.deltaTime;
    transform.position = new Vector2(position.x, position.y);

碰撞时,如果你坚持修改物体的位置属性使碰撞物体相交,那么你就会得到一个抖动的物体(抖出残影也不稀奇
考虑一个与现实类似的情况,你把靠在墙上的物体的坐标改到墙里面,那个物体在这一个时刻与墙相交,但下一个程序检查的时刻就会被弹出来,因此在视觉上造成了物体的抖动
为了避免这个问题,就不能在碰撞后直接修改物体的位置坐标你从玩家那里得到的输入只有一个速度方向,如果你坚持使用修改物体的位置坐标的方式,那么你必须模拟物体对碰撞边界的判断,这将是一个有点繁琐的工程
那么你现在知道除了transform.position,还有一个修改物体位置的方式,那就是给物体加上一个刚体组件,在脚本内新建一个Rigidbody2D类型的变量以获取到物体的刚体组件,然后使用 刚体变量.MovePosition这个方法为什么得以实现?看上去 刚体变量.MovePosition并没有返回值,实际上经过GetComponent之后这个刚体变量就可以看作是物体的一个属性了这个方法为什么可以消除抖动,因为刚体变量.MovePosition之中已经在刚体运动的基础上计算好了正确的移动位置,计算正确了再移动,自然不会先卡入墙中,没有卡入墙中就没有弹出,也就没有抖动了

在使用moveVector为角色运动动画树传入参数后,还可以将运动代码进一步简化:

    moveX = Input.GetAxisRaw("Horizontal");
    moveY = Input.GetAxisRaw("Vertical");
    Vector2 moveVector = new Vector2(moveX, moveY);
    //用刚体移动可以消除画面抖动
    rbody.MovePosition(rbody.position+ speed * moveVector * Time.fixedDeltaTime);

二、
使用cinemachine绑定了人物之后仍然无法跟随?
可能是你的相机设置的范围过大,CameraFrontier刚好包住相机

三、
关于sleeping mode
休眠是一种优化方式,当刚体静止时,可以暂时从物理模拟中删除对象。
此属性选择刚体是否应该开始时休眠、唤醒或完全关闭休眠。
因此当你需要一个物体在静止时也一直参与物理模拟的话(比如角色站在陷阱中持续掉血),你可以选择sleeping mode的never sleep
四、如果你遇到了类似如下提示的错误,但是你的游戏仍然能运行那么你可以尝试重启unity(也就是说,这不是你的脚本代码的问题
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at D:/unity/Editor/Graphs/UnityEditor.Graphs/Edge.cs:114)

五、
我这个ruby的代码很大一部分是抄csdn上的
因为我已经重复打过好多次了,不想再打了
然后我就去复制粘贴嘛,然后就看到这个博主在敌人受到主角攻击转变成被修复的形态的代码那里,注释:这样会导致第二个机器人无法运动
我就很奇怪了,教程里好像没有出现这种情况啊,我就去看了一下,刚刚才发现原来是他的变量类型设置错误了,isFixed设置成静态的了
这个isFixed就是判断敌人是否被修复,他设置成静态,就是说所有敌人实例都只看一个参数,这一个变量改了,所有敌人也跟着变
如果这个isFixed只是普通的成员参数,那么就是所有敌人实例都有自己的变量,各自的变量控制各自的状态

六、
现在要做一个角色行走时的音效
关键点是,update()在每一帧循环,如果我根据角色行走时的速度不为0为标志播放音效,那么每一个行走帧都会播放一次音效,那么就会很吵
应该有一个检测,检测到某个音频是否已经播放过
最方便的方法就是做一个计时器这是AudioManager脚本,挂在一个空物体上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioManager : MonoBehaviour
{
    public static AudioManager instance { get; private set; }

    private AudioSource audioS;

    private float clipPlaytimer;
    private bool isclipPlaying;

    // Start is called before the first frame update
    void Start()
    {
        instance = this;
        audioS = GetComponent<AudioSource>();

        clipPlaytimer = 0;//初始化计时器
        isclipPlaying = false;//初始未播放音频
    }

    // Update is called once per frame
    void Update()
    {
        //如果已播放过,则计时器运作
        if (isclipPlaying == true)
            clipPlaytimer -= Time.deltaTime;
        if (clipPlaytimer < 0)
            isclipPlaying = false;
    }

    public void AudioPlay(AudioClip clip)
    {
        audioS.PlayOneShot(clip);
    }
    public void AudioPlayNoOverLap(AudioClip clip)
    {
        //该函数只能被同时使用一次,因为计时器只有一个,可以改进

        //如果已经播放了clip,直接返回
        if (isclipPlaying == true)
            return;
        audioS.PlayOneShot(clip);
        //设置成已播放过clip
        isclipPlaying = true;
        clipPlaytimer = clip.length;
        Debug.Log("开始播放不重叠音频"+ clip.length+"s");
    }
}

这是PlayerController脚本的一部分

        //获取运动矢量
        moveX = Input.GetAxisRaw("Horizontal");
        moveY = Input.GetAxisRaw("Vertical");
        Vector2 moveVector = new Vector2(moveX, moveY);
        //}

        //用刚体移动可以消除画面抖动
        rbody.MovePosition(rbody.position+ speed * moveVector * Time.fixedDeltaTime);

        //=================通过运动速度判断角色运动动画分支=================

        if (moveX != 0 || moveY != 0)
        {
            //角色运动时,覆盖角色朝向
            lookDirection = moveVector;
            AudioManager.instance.AudioPlayNoOverLap(walkClip);
        }
        

        //为动画集赋值
        //状态集的切换由运动矢量决定
        anim.SetFloat("Look X", lookDirection.x);
        anim.SetFloat("Look Y", lookDirection.y);
        anim.SetFloat("Speed", moveVector.magnitude);
        //赋值必须与状态集命名一样

七、
发现一个交互上的问题
玩家角色是通过发射射线,射线与NPC碰撞进而得到NPC信息的。当玩家角色靠近可交互NPC时,射线并不总是能击中NPC,只有控制角色与NPC成一个相对位置时才能击中。也就是说,当玩家控制它的角色走到了NPC身边,也不一定可以进入交互,这是破坏游戏体验的

八、
unity的inspector属性数值设置优先于脚本的成员变量初始化且不同步
不管这个变量是public还是[SerializeField] private
比如,你在脚本里设置public int count=1;
回到unity挂载脚本之后,在inspector里面设置了count=5
你会发现脚本里代码仍然是public int count=1;没有变
但是游戏运行时使用的是inspector里面设置的count=5

九、
[SerializeField]的作用就是使得脚本内private类型的变量在unity的inspector面板上可见并可修改

十、
按照教程,我将树的资源图片的Pivot设置成Bottom,再将树的预制体的Sprite Sort Point改成Pivot,角色的资源图片的Pivot也是Bottom Center,人和树的layer相同就是做不出来那个角色一半进入树一半留在树的外面的穿透效果,不知道为什么