一.人物控制
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初学者,只是希望能记录一些笔记和心得,如果有任何有问题的地方,希望可以指正。
本文详细介绍了Unity中角色控制(包括镜头跟随、翻转和动画衔接)、金币计数与音效、文本框实现、移动平台功能以及场景切换的方法,还提到了一些常见问题的解决方案,如角色跳跃卡顿和音频设置。

4767

被折叠的 条评论
为什么被折叠?



