第一次接触Unity,并且跟着视频资料做出了第一款2D像素游戏,因为还有很多不懂的地方,以及在学习过程中感觉有待优化的地方,所以想要通过这篇文章对自己进行一个总结,内容其实都是写给以后的自己。
在学习过程中呢,有收获,也遇到问题,这些呢,有可以分为编写代码类,和Unity使用类的
这是一款简单的2D像素横板游戏,可以收集物品,消灭敌人,在移动上实现站立,跑动,下蹲,跳跃。
1.Tilemap
2.Animation
3.Cinemachine
4.收集物品
5.消灭敌人
6.角色摩擦
6.UI
public void PauseGame()
{
pauseMenu.SetActive(true);
//时间比例
Time.timeScale=0f;
}
public void SetVolume(float value)
{
audioMixer.SetFloat("MainVolume",value);
}
7.AI
8.Audio Source
新建GameObject,重命名为SoundManager
public static SoundManager instance;
public void Awake()
{
instance=this;
}
例如:人物脚本中调用音效
SoundManager.instance.CollectionAduio();
接着是在Audio Mixer中新建Groups,选择为音效的Output
9.2D光效
使用的是2018版本,如果是高版本的可以使用Universal RP
1.Update()和FixedUpdate()
2.跳跃(可适用多数2D横版游戏)
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
private Rigidbody2D rb;
private Collider2D coll;
private Animator ani;
public float speed,jumpForce;
public Transform groundCheck;
public LayerMask ground;
public bool isGround,isJump;//是否在地上,是否在跳跃状态
bool jumpPressed;//跳跃键是否摁下
int jumpCount;
// Start is called before the first frame update
void Start()
{
rb=GetComponent<Rigidbody2D>();
coll=GetComponent<Collider2D>();
ani=GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if(Input .GetButtonDown("Jump")&&jumpCount>0)
{
jumpPressed=true;
}
}
private void FixedUpdate() {
//Player下部有一个点groundCheck检查是否在地面上
isGround=Physics2D.OverlapCircle(groundCheck.position,0.1f,ground);
Movement();
Jump();
}
void Movement()
{
//移动
float horizontalmove=Input.GetAxisRaw("Horizontal");
rb.velocity=new Vector2(horizontalmove*speed,rb.velocity.y);
//转向
if(horizontalmove!=0)
{
transform.localScale=new Vector3(horizontalmove,1,1);
}
}
void Jump()
{
if(isGround)
{
jumpCount=2;
isJump=false;
}
if(jumpPressed&&isGround)
{
isJump=true;
rb.velocity=new Vector2(rb.velocity.x,jumpForce);
jumpCount--;
jumpPressed=false; //证明这段代码已经执行完
}else if(jumpPressed&&jumpCount>0&&!isGround){//!isGround同理isJump,都是判断在空中,前者比后面多一个范围是从高空平台掉落
rb.velocity=new Vector2(rb.velocity.x,jumpForce);
jumpCount--;
jumpPressed=false;
}
}
}
这个代码优化手感的主要想法是根据Input.GetB uttonDown和FixedUpdate。
Input.GetButtonDown
Call this function from the Update function, since the state gets reset each frame. It will not return true until the user has released the key and pressed it again.
这样按下跳跃键可以第一时间收到信号,接着是优化手感的精髓,在FixedUpdate里调用的移动,跳跃的代码,这样每一固定的帧反馈给玩家就是最即时的状态,手感就会很流畅。
3.翻转朝向
float horizontalmove=Input.GetAxis("Horizontal");
//获得数字型的数值[-1,1]
float facedirection=Input.GetAxisRaw("Horizontal");
//获得三个数字型的数值-1,0,1
if(facedirection!=0)
{//正面方向
rb.transform.localScale=new Vector3(facedirection,1,1);
}
4.跑步动画向下蹲动画过渡
为了贴近真实,按下下蹲键之后,人物的移动速度也就下降,这样在跑步的过程中,会有一个减速的过程,在速度降到一定值之后,会转换到下蹲动画
rb.velocity=new Vector2(horizontalmove*speed*Time.fixedDeltaTime*(anim.GetBool("crouching")?0.5f:1),rb.velocity.y);
5.跑步动画停止
设定值的时候如果用facedircetion去给running设定值的话,玩家一放开键盘方向键的时候run的动画就会立刻停止,而用horizontalmove去给running设定值的话是当小狐狸停下来的时候run的动画才会停止。
anim.SetFloat("running",Mathf.Abs(facedirection));
跑步的方法确实很多,举个例子,这里可以使用int或bool代替float,判断跑步时running赋值为1,否则赋值为0,动画切换条件就是看running是一还是零。这样就可以不用一直调用函数Abs,减少了运算负担
6.敌人死亡动画播放
个人感觉敌人的死亡动画,是一种比较特殊的动画,①因为在播放这个动画同时,需要考虑原先存活敌人的判定框,再次触碰会重复播放死亡动画,②死亡动画会根据原先的轨迹继续移动,③动画结束最后要销毁敌人项目。想要解决以上的问题,分别需要实现,判断击杀敌人之后删除碰撞体;锁定x,y轴(游戏是2D横板,z轴已经锁定);最后播放死亡动画,在动画的结束添加event事件,调用销毁项目的函数。实测有效代码如下:
public void JumpOn()
{
//打开开关,播放死亡动画
anim.SetTrigger(Death);
//删除碰撞体
gameObject.GetComponentCollider2D().enabled = false;
//锁定x,y,z轴
gameObject.GetComponentRigidbody2D().constraints = RigidbodyConstraints2D.FreezeAll;
}
8.弹出提示框
为人物添加Player的Tag,添加碰撞器在希望弹出提示框的地方,并且将is Trigger勾选,同时挂载脚本,识别人物的碰撞体
public GameObject enterDialog;
void OnTriggerEnter2D(Collider2D other) {//碰撞事件,提示框弹出
if(other.tag=="Player")
{
enterDialog.SetActive(true);
}
}
private void OnTriggerExit2D(Collider2D other) {//离开事件,提示框消失
if(other.tag=="Player")
{
enterDialog.SetActive(false);
}
}
为提示框添加渐变动画
-Add Animation ,在Ainmation里面点击Preview,可以录制动画,在不同时间间隔之后,将提示框的背景色逐步加深,最后弹出文本,可以实现渐变效果
9.进入下一关卡
当人物移动到指定位置,或者达成某一条件之后,按下指定的键,就会进入下一场景。
void Update()
{
if(Input.GetKeyDown(KeyCode.E))
//跳转到当前场景序号的下一个场景
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex+1);
}
}
在File -> Build Settings 中,将制作好的关卡场景多选拖进之后,会在后面显示出,该场景的序号
10.重生
掉落死亡,会触发重生,延迟运行&&重启当前场景。新建一个GamObject,添加碰撞器,调整大小至人物掉落出场景后会碰撞到,勾选isTrigger,新建并设置Tag为DeadLine
if(collision.tag=="DeadLine")
{
//Invoke(Restart,2f),同理延迟2秒后调用函数
Invoke(nameof(Restart),2f);
}
void Restart()
{ //重新加载当前场景
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
在某些情况下,需要记录位置,返回上衣场景之前的位置,类似于CheckPoint
11.地图更加有层次感
背景图片并不是固定,设定一个范围值(0,1),使图片跟着摄像头在范围之内移动。可以选择是否锁定Y轴。
public Transform Cam;
public float moveRate;
private float startPointX,startPointY;
public bool LockY;//false
// Start is called before the first frame update
void Start()
{
startPointX=transform.position.x;
startPointY=transform.position.y;
}
// Update is called once per frame
void Update()
{
if(LockY)
transform.position=new Vector2(startPointX+Cam.position.x*moveRate,transform.position.y);
else
transform.position=new Vector2(startPointX+Cam.position.x*moveRate,startPointY+Cam.position.y*moveRate);
}
收集物品计数异常
因为任务添加了方形和圆形两个碰撞器,会偶尔发生一种情况,两个碰撞器同时触碰到同一个触发器(同一个樱桃),就会执行两次计数。
受伤的逻辑判断
个人的思路是,使用Trigger控制受伤动画的播放,Any State到受伤状状态,受伤动画只播放一次,(loop time取消勾选),之后连线切换到idel状态
敌人没有完善的AI
目前敌人只是在设定好的范围内反复移动,如果人物和敌人出现在同一屏幕内,敌人并不会追踪人物。暂时还没有实现的思路
暂停菜单
①按下暂停按键之后,Time.timeScale=0f;但是如果按下跳跃键,会进入跳跃状态,播放跳跃动画。②在跳跃的时候不能点击暂停键