Unity 2D横板闯关游戏部分功能的实现方法

本文详细介绍了Unity中角色控制(包括镜头跟随、翻转和动画衔接)、金币计数与音效、文本框实现、移动平台功能以及场景切换的方法,还提到了一些常见问题的解决方案,如角色跳跃卡顿和音频设置。

一.人物控制

1.镜头的跟随与限制

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

public class CameraFollow : MonoBehaviour
{
    public Transform target;//获取角色
    public float smoothing;

    public Vector2 minPosition;//最小位置
    public Vector2 maxPosition;//最大位置

    void LateUpdate()
    {
        if (target != null)
        {
            if(transform .position != target .position)//镜头的位置与人物位置不重合时
            {
                Vector3 targetPos = target.position;
                targetPos.x = Mathf.Clamp(targetPos.x, minPosition.x, maxPosition.x);//如果给定值(targetPos)小于最小值,则返回最小值。如果给定值大于最大值,则返回最大值。
                targetPos.y = Mathf.Clamp(targetPos.y, minPosition.y, maxPosition.y);
                targetPos.z = transform.position.z;
                transform.position = Vector3.Lerp(transform.position, targetPos, smoothing);//线性插值,在前两者(镜头和人物)之间插入数值,使其更加平滑
            }

        }
        
    }

    public void SetCamPosLimit(Vector2 minPos,Vector2 maxPos)
    {
        minPosition = minPos;
        maxPosition = maxPos;
    } 
}

2.人物左右移动时的翻转

private Rigidbody2D myRigidbody;

void Start()
 {
     myRigidbody = GetComponent<Rigidbody2D>();
 }

void Flip()
{
    bool PlayerHasXAxiSpeed = Mathf.Abs(myRigidbody.velocity.x) > Mathf.Epsilon;//Abs为返回绝对值,veclocity为刚体的速度,Epsilon为微小浮点数。
    if (PlayerHasXAxiSpeed)
    {
        if(myRigidbody .velocity .x >0.1f)
        {
            transform.localRotation = Quaternion.Euler(0,0,0);//相对于人物本身的变换旋转角度。Quaternion.Euler意思是相对旋转的角度(x,y,z)。
        }
        if (myRigidbody.velocity.x < -0.1f)
        {
            transform.localRotation = Quaternion.Euler(0, 180 , 0);
        }
    }
}

3.人物基础动画的衔接与逻辑

 public float movespeed;
 public float jumpspeed;

 private BoxCollider2D myFeet;

 private bool IsGround;
 private bool IsJumping;
 private bool IsFalling;

 private Animator myAnim;

 void Start()
 {
     myRigidbody = GetComponent<Rigidbody2D>();
     myAnim = GetComponent<Animator>();
     myFeet = GetComponent<BoxCollider2D>();

 }
void Update()
{
    CheckGrounded();
    Run();
    Flip();
    Jump();
    SwitchAnimation();

}

 以上为变量的设置情况。

 1.首先,我们需要检测的代码,例如检测是否在地面,空中等。

void CheckGrounded()
{
    IsGround = myFeet.IsTouchingLayers(LayerMask.GetMask("Ground"));
    //Debug.Log("IsGround");
}

其中myFeet即为我们给角色绑定的脚部碰撞体 (并且设置为Is Trigger)。当他接触到名为Ground的遮罩层后,IsGound即为True。

那么我们需要将Layer变更为Ground。并且给地面添加Box Collider 2D的组件,设置好碰撞范围。 


 2.其次,我们需要动画的绑定。

我们需要有动画,并且打开Animator界面。

在左侧的Parameters中设置一些 bool变量;

这需要与下面代码中的myAnim.SetBool("Run",true);一致才可以。

 void Run()
 {
     float moveDir = Input.GetAxis("Horizontal");//按左右方向键移动
     Vector2 playerVel = new Vector2(movespeed * moveDir  , myRigidbody.velocity.y);
     myRigidbody.velocity = playerVel;
     bool PlayerHasXAxiSpeed = Mathf.Abs(myRigidbody.velocity.x) > Mathf.Epsilon;
     myAnim.SetBool("Run",PlayerHasXAxiSpeed);
 }
 void Jump()
 {
     if (Input .GetButtonDown ("Jump"))
     {
         if (IsGround)//如果在地面,那么Jump为真,给y轴给予速度
         {
             myAnim.SetBool("Jump", true);
             Vector2 jumpVel = new Vector2(0, 1);//坐标单位化,有利于改变jumpspeed时更加容易掌握
             myRigidbody.velocity = jumpVel * jumpspeed;
         }
         
     }

 }

3.然后,我们还需要动作的衔接。

void SwitchAnimation()
{
    myAnim.SetBool("Idle", false );//设置bool
    if (myAnim.GetBool("Jump"))//获取bool,如果人物在跳跃中,并且y轴速度小于0,也就是说人物在下降。
    {
        if(myRigidbody .velocity .y < 0)
        {
            myAnim.SetBool("Jump", false );
            myAnim.SetBool("Fall", true);
        }
    }
    else if (IsGround)//如果在地面
    {
        myAnim.SetBool("Fall", false);
        myAnim.SetBool("Idle", true);
    }
    else if(myAnim .GetBool ("Run") && myRigidbody.velocity.y < 0)//如果人物在奔跑,并且正在空中下落,保证了从高处下落也会有动画实现,而不仅仅时跳跃中会实现。
    {
        myAnim.SetBool("Fall", true );
        myAnim.SetBool("Idle", false );
    }
}

4.最后我们需要逻辑的设置。

点击animator界面右侧的Transition线,就可以在Inspector中看到这个界面,一般来讲,Setting中的Exit Time和Transition Duratio/Offset均设置为0即可。之后就可以在Conditions中添加刚才设置好的bool值作为条件。

4.人物爬梯子的动画设置

public float climbspeed;

private bool IsLadder;
private bool IsClimbing;

private Rigidbody2D myRigidbody;
private BoxCollider2D myFeet;

private Animator myAnim;

private float PlayerGravity;

void Start()
{
    myAnim = GetComponent<Animator>();
    myFeet = GetComponent<BoxCollider2D>();
    myRigidbody = GetComponent<Rigidbody2D>();

    PlayerGravity = myRigidbody.gravityScale;//保存最开始的重力信息
}

void Update()
{
    CheckLadder();
    CheckAirStatus();
    Climb();
}

void CheckLadder()
{
    IsLadder = myFeet.IsTouchingLayers(LayerMask.GetMask("Ladder"));
}

void Climb()
{
    if (IsLadder)//如果在梯子上时,输入w/向上方向键,如果向上或者向下大于阈值。
    {
        float moveY = Input.GetAxis("Vertical");
        if (moveY >0.5f||moveY < -0.5f)
        {
            myAnim.SetBool("Climbing",true );//Climbing为真
            myRigidbody.gravityScale = 0.0f;//重力为0,
            myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, moveY * climbspeed);

        }
        else//如果不超过阈值
        {
            if(IsJumping || IsFalling)//如果在跳或者在降落
            {
                myAnim.SetBool("Climbing", false );//不执行Climbing,为假。
            }
            else//静止在梯子上时
            {

                myAnim.SetBool("Climbing", false);
                myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, 0.0f);
            }
        }
    }
    else//如果不在梯子上
    {
        myAnim.SetBool("Climbing", false);
        myRigidbody.gravityScale = PlayerGravity;//恢复之前的重力效果。
    }
}

void CheckAirStatus()
{
    IsJumping = myAnim.GetBool("Jump");
    IsClimbing  = myAnim.GetBool("Climbing");
    IsFalling = myAnim.GetBool("Fall");
}

注意:由于我是以功能为导向,所以2,3,4中的代码可以合并为一个脚本,并赋给人物。

二. 金币的获取,计数与音效

1.如何创建一个UI,并且如何计数。

首先在Unity中我们创建UI中找到Canvas,在其中创建一个Text文本,组件一般用Text,不用TextMeshPro(因为很多视频教程比较老),再将创建Image将图标导入,并将这两个放在你认为合适的地方。

下面的创建也是为了服务于上方的Text与Image。静态的CurrentCoinNumber是为了能够持续记录金币获取的数量。 

using UnityEngine.UI;//需要添加这个库

public class CoinUI : MonoBehaviour
{
    public int startCoinNumber;//初始金币值
    public Text CoinNumber;//添加一段文本

    public static int CurrentCoinNumber;//静态的当前金币数

    void Start()
    {
        CurrentCoinNumber = startCoinNumber;
    }

    void Update()
    {
        CoinNumber.text = CurrentCoinNumber.ToString();
    }
}

2.接下来就需要添加音效。

以上是金币的绑定,因为我看的教程是如此,所以为了能添加音效,所以做了一些改变,跟网上的做法不同。

首先父级添加了脚本和碰撞体,子级添加了动画和材质,这样做可以参考下面的代码,如果Player触碰了金币,那么金币数字加1,并且播放音效,如果使用destroy方法,就会销毁,同时不能执行音效,所以,只能让图层消失,并且销毁碰撞体,这样既可以播放音效又可以使金币消失。(不建议这种方法,因为会占用内存,增加无关的性能损耗) 


public class CoinItem : MonoBehaviour
{
    private AudioSource coinAudio;
    public AudioClip coinClip;//添加一段拾取音频
    public  GameObject coin;

    void Start()
    {
        coinAudio = GetComponentInParent<AudioSource>();
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.CapsuleCollider2D")
        {

            CoinUI.CurrentCoinNumber += 1;
            coinAudio.clip= coinClip;
            coinAudio.Play();//播放音乐
            coin.SetActive(false);//金币消失(图层消失,但并不是销毁,依然存在在游戏中)
            GetComponent<BoxCollider2D>().enabled = false;//防止连续碰撞
        }

    }

}

 如果玩家需要听到音效或者背景音乐,需要给相机添加Audio Listener的组件即可。

三.文本框的实现

using UnityEngine.UI;
public class Sign : MonoBehaviour
{
    public GameObject dialogBox;
    public Text dialogBoxText;
    public string signText;//字符串
    public string startText;
    private bool isPlayerInSign;

    void Start()
    {
        dialogBoxText.text = startText;
    }

    void Update()
    {
        if (isPlayerInSign)
        {
            dialogBox.SetActive(true);//文本框出现(图层)
        }
        if(Input.GetKeyDown (KeyCode.E) && isPlayerInSign)//输入E键,切换下一个文本
        {
            dialogBoxText.text = signText;
        }
    }
    void OnTriggerEnter2D(Collider2D other)//进入检测
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.CapsuleCollider2D")//限制为胶囊体碰撞,防止多次触发
        {
            isPlayerInSign = true;//玩家在范围内
        }
    }
    void OnTriggerExit2D(Collider2D other)//退出检测
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.CapsuleCollider2D")
        {
            isPlayerInSign = false;
            dialogBox.SetActive(false);//文本框消失
        }
    }

}

无论是文本框的出现,还是角色掉下悬崖出现GameOver等等,这种人物进入某个范围出现某个东西的功能,其实都可以使用上面的代码稍加改动即可。范围的设置通过改变碰撞体的范围就可以,但需要注意改为Is Trigger。

四.移动平台功能

public class MovePlatform : MonoBehaviour
{
    public float Speed;
    public float waitSpeed;
    public Transform[] movePos;//创建一个移动位置的坐标

    private int i;
    private Transform playerDefTransform;

    void Start()
    {
        i = 1;
        playerDefTransform = GameObject.FindGameObjectWithTag("Player").transform.parent;
    }

    void Update()
    {
        transform.position = Vector2.MoveTowards(transform.position, movePos[i].position, Speed * Time.deltaTime); 
//返回一个transform.position向movePos[i].position靠近的值,靠近多少呢?由Speed * Time.deltaTime决定。
//若Speed * Time.deltaTime < 0,则返回的浮点数将从transform.position开始远离movePos[i].position;
//若Speed * Time.deltaTime >= movePos[i].position,返回的浮点数为movePos[i].position;
        if (Vector2 .Distance (transform .position ,movePos[i].position )<0.01f)//十分靠近
        {
            if (waitSpeed <0.0f)//没有停止
            {
                if (i == 0)//这一步相当于实现移动平台的来回移动,位置的变化
                {
                    i = 1;
                }
                else
                {
                    i = 0;
                }
                waitSpeed = 3f;
            }
            else//停止了
            {
                waitSpeed -= Time.deltaTime;//随时间递减
            }
        }
    }
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.BoxCollider2D")
        {
            other.gameObject.transform.parent = gameObject.transform;//将角色变更为平台的子级
        }
    }
    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.BoxCollider2D")
        {
            other.gameObject.transform.parent = playerDefTransform;//恢复初始所属关系
        }
    }
}

 

这就是所创建的Move Pos(位置坐标),创建两个空物体(更改其坐标),将两个空物体赋于两个元素中,就形成平台运动的两端。 

五.场景之间的切换

创造一个新的场景(scenes),然后将第一个场景中的素材可以做成预制体,这样就可以在第二个场景中使用。

在File中打开Build Setting。就可以看到以下界面,右边的Deleted就代表了场景的顺序,在下面代码中+1也就可以理解了。

public class DoorToNextScence : MonoBehaviour
{
    private bool isPlayerInSign;
    public Text dialogBoxText;
    public string startText;
    public GameObject dialogBox;

    void Start()
    {
        dialogBoxText.text = startText;
    }

    void Update()
    {
        if (isPlayerInSign)
        {
            dialogBox.SetActive(true);//出现对话框
        }
        if (Input.GetKeyDown(KeyCode.E) && isPlayerInSign)
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);//切换至下一场景
           
        }
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject .CompareTag ("Player")&&other .GetType().ToString()== "UnityEngine.CapsuleCollider2D")
        {
            isPlayerInSign = true;

        }

    }
    void OnTriggerExit2D(Collider2D other)
    {
        if (other.gameObject.tag == ("Player") && other.GetType().ToString() == "UnityEngine.CapsuleCollider2D")
        {
            isPlayerInSign = false;
            dialogBox.SetActive(false);//文本框消失
        }
    }
}

六.注意

1.人物在起跳和下落时,会出现卡在墙壁的情况。

最简单的方式是改变人物摩擦力,将材质球的Friction和Bounciness全部改为0,并且赋给胶囊体材质即可。

2.背景音乐的有关问题

需要创建一个空物体,给他添加Audio Sourse组件

勾选Loop(循环),这样就会一直有声音存在,也可以勾选Play On Awake,这样一进入游戏就会播放音乐 ,然后将音乐素材赋予Audio Clip。

3.有关预制体

1.概念

将物体第一次拖入Inspector中,形成原始预制体

若再次拖入,就会形成预制体变体

2.关系

1.改变Hierarchy中的预制体实例不会影响原始预制体。
2.改变原始预制体会影响预制体实例。
3.预制体变体就是原始预制体的子类,可以与原始预制体有不同的地方,不同的地方不受原始预制体的影响。

 七.结语

作为一位Unity初学者,只是希望能记录一些笔记和心得,如果有任何有问题的地方,希望可以指正。

资料来源:

1.B站up主秦无邪OvO;

2.Unity官方API和资料;

3.自己的心得体会;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海海不瞌睡(捏捏王子)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值