小狐狸横版游戏开发学习笔记(上)
目录
6.动画转换(有部分是其它项目中的截图,放在一起是为了更全面的理解)
1.关于如何创建Tilemap
2.关于地图格子之间出现间隙的问题
将地图间隙修改为-0.001
3.如何设置自己想要的控制按键
4.如何解决玩家移动过程中造成的卡顿问题
首先需要添加一个组合碰撞体Composite Collider 2D,添加完之后会自动生成一个刚体组件
将刚体组件中的Body Type属性由原来的Dynamic改为Static,然后将Geometry Type的属性改为Polygons(相当于将碰撞体设置为了实心),若设置为outlines,当角色速度过快时,容易发生穿模。
勾选used by composite(复合碰撞体)
5.如何添加动画
首先需要将角色挂接上Animator组件
其次在Assets资源中新建一个Animation文件夹,用来存放Animator Controller
(这里新建了一个Animation Controller,并将其重命名为Player)并将其挂接至Animator上
打开Animation窗口(ctrl+6)先点击所要挂接对象(这里是Player),再点击Create,创建动画
选中每帧的动作,将其像素改成相应的大小,再将其选中拖入时间轴
如果速度过快,则将Samples(帧频)的大小改小
打开Animator窗口
6.动画转换(有部分是其它项目中的截图,放在一起是为了更全面的理解)
增加一个变量,用于控制状态转换
取消勾选Has Exit Time,因为在这里我们需要角色立刻退出当前状态,不需要延时
把Transition Duration改成0,因为我们不需要转换时间,我们希望它立刻改变状态
Runright状态转换成idle的时候,需要将greater改为less
如果想要建立BlenderTree,则右键Create new BlendTree
能够根据不同的参数来调整同一状态的的不同方向动画,下面是添加参数的流程
这样才能将参数挂上去,如下所示
然后根据代码,将数值传入
7.如何解决,玩家与地图穿插显示在页面最前方的问题
由于小狐狸和tilemap(地图)都在同一个图层,将order in layer改为负数,代表地图在小狐狸的后方,这样就不会出现小狐狸穿过梯子时,不断闪烁的问题
若想查看任务状态的转换,可以开启Base Layer和Game同步,这样就可以清晰地看清楚,不同状态之间的转换,如果要看动画状态的转换,需要先选中角色
8.如何解决下落转换为初始状态太慢的问题
由于发现 通过判断纵向速度是否为0十分不便,player.velocity.y是浮点数,让其判断为0时从falling状态改为idling状态,会产生延迟现象,所以我们需要通过设置:判断当玩家处于下落状态,并碰到地面时,玩家状态便会立即转换成idling,以下是操作步骤:
我们需要利用定义LayerMask,用来获得地面的信息
Collider2D 用来获取到小狐狸的碰撞体
将Tilemap加至ground图层
将我们创建的用来获取地面信息的变量,改成Ground图层,由于Ground图层表示的是tilemap,因此我们就获得了地面信息
将小狐狸的碰撞体挂接到coll上
然后我们通过代码,小狐狸的碰撞体是否碰到了地面图层
如果碰到地面图层,idling状态为true,falling状态为false
9.关于镜头的移动
为Main Camera添加脚本,让摄像机的位置随着角色而移动,所以我们需要让摄像机获取小狐狸的位置x与y,z不变为-10;
若想要在一定的范围内角色不动,超过一定范围之后,镜头才随着角色的移动而移动,我们需要下载一个插件camerachine,如下图所示
将角色挂接到相机的follow对象,改变body中的Dead Zone Width和Height
能够控制角色位置移动,但相机不跟随的范围,步骤如下:
10.如果不想让镜头超出边界
为新添加的镜头增加扩展,CinemachineConfiner,限制镜头的位置
此时会有一个Bounding Shape 2D需要挂接collider 2D(表示不能出的范围区域)
由于我们想要的是范围不出background,所以我们需要为background添加polygon Collider 2D
并点击Edit Collider来设置范围,摁住ctrl再点击点,可以去掉该点
将Background挂接,并将is Trigger勾选,否则小狐狸会因为碰撞体相互碰撞被弹出该范围
下面为部分代码展示
小狐狸:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
//[SerializeField]Rigidbody2D player; 前面加上SerializeField能够将该变量显示出来,但是无法更改
Rigidbody2D player;
public float speed = 5.0f;
public float jumpforce = 3.0f;
Animator anim;
public LayerMask ground;
public Collider2D coll;
private int cherry = 0;
private int gem = 0;
public Text charryNumber;
public Text gemNumber;
private bool isHurt;//默认是false
public AudioSource jump;
public AudioSource cherrySound;
public AudioSource gemSound;
public AudioSource hitSound;
float horizontalmove;//只有三种情况会出现:1、-1、0,右为正,左为负,不动为零
float facedirection;
// Start is called before the first frame update
void Start()
{
player = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if(!isHurt)//当玩家没有受伤时,才调用Movement,防止角色碰到敌人后不会被弹开
{
Movement();
}
Getposition();//每次刷新时都获取位置
Switchanim();
charryNumber.text = cherry + "";//将charry的值转变为字符串型赋给charryNumber的text,同时能够使用cherry.ToString()
gemNumber.text = gem.ToString();//利用了另一种表达,将int类型变为字符串类型
}
private void FixedUpdate()
{
}
void Getposition()//获取位置
{
horizontalmove = Input.GetAxis("Horizontal");//获取水平移动
facedirection = Input.GetAxisRaw("Horizontal");//获取面朝的方向,左为-1,将该值给到scale的x,即可改变面朝像
//if (Input.GetButtonDown("Jump"))
//{
// player.velocity = new Vector2(player.velocity.x, 5);
//}
}
void Movement()
{
if (horizontalmove!=0)
{
player.velocity = new Vector2(horizontalmove*speed, player.velocity.y);//player.velocity表示玩家的速度,第一个是水平方向,第二个是竖直方向
anim.SetFloat("running",Mathf.Abs(horizontalmove));
}
if(facedirection!=0)
{
player.transform.localScale = new Vector3(facedirection,1,1);//将该值给到scale的x,即可改变面朝像
}
//跳跃
//coll.IsTouchingLayers(ground)可换成anim.GetBool("idling")|| anim.GetBool("running")
if (Input.GetButtonDown("Jump")&& coll.IsTouchingLayers(ground))
{
player.velocity = new Vector2(player.velocity.x, 8);
jump.Play();
anim.SetBool("jumping",true);
}
//Vector2 position = player.position;
//position.x = player.position.x + horizontalmove * speed * Time.deltaTime;
//player.MovePosition(position);//等于player.position=position;
}
void Switchanim()
{
if (player.velocity.y < 0.1f && !coll.IsTouchingLayers(ground))
{
anim.SetBool("falling", true);
}
anim.SetBool("idling",false);
if (anim.GetBool("jumping"))
{
if( player.velocity.y < 0)
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);
}
}
else if (isHurt)//必须要加在else if (coll.IsTouchingLayers(ground))之前,因为人物一直挨着地面,就不会进行到下一个的if条件判断
{
if (Mathf.Abs(player.velocity.x) < 0.1f)
{
isHurt = false;
anim.SetBool("hurt", false);
anim.SetBool("idling",true);
}
}
else if (coll.IsTouchingLayers(ground))
{
anim.SetBool("idling", true);
anim.SetBool("falling", false);
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag=="collection")
{
cherrySound.Play();
Destroy(collision.gameObject);
cherry += 1;
}
if (collision.tag == "collection2")
{
gemSound.Play();
Destroy(collision.gameObject);
gem += 1;
}
}
private void OnCollisionEnter2D(Collision2D collision)//由于不能将敌人勾选is trigger,如果勾选,敌人将会直接掉出地图之外,所以这个地方不能使用OnTriggerEnter2D函数
{
if (collision.gameObject.tag == "Enemies")//OnCollisionEnter2D与OnTriggerEnter2D不同,它需要先取到它本身,再取出tag,所以写为collision.gameObject.tag
{//当小狐狸碰撞到敌人时
Enemy enemy = collision.gameObject.GetComponent<Enemy>();
if (anim.GetBool("falling"))//同时当小狐狸处于下落状态时,碰撞敌人后,敌人消失
{
enemy.Death();
player.velocity = new Vector2(player.velocity.x, 7);//敌人消失后,做一个跳跃的动作丰富场景
//anim.SetBool("jumping", true);我认为这句话没什么用,因为在动画转换中根本没有下落转跳跃的箭头
}
//下面是教程中给的方法
else if (transform.position.x > collision.gameObject.transform.position.x)
{
hitSound.Play();//当小狐狸不是下落碰撞时,会被弹走
player.velocity = new Vector2(5, player.velocity.y);
isHurt = true;//由于程序是在不断刷新的,Movement不断地在被调用,不摁住键盘输入指令,小狐狸就不会动,也不会被弹开,所以需要用isHurt来控制Movement是否执行(在update与Switchanim中用if语句控制)
anim.SetBool("hurt", true);
anim.SetFloat("running", 0);
}
else if (transform.position.x < collision.gameObject.transform.position.x)
{
hitSound.Play();
player.velocity = new Vector2(-5, player.velocity.y);
isHurt = true;//由于程序是在不断刷新的,Movement不断地在被调用,不摁住键盘输入指令,小狐狸就不会动,也不会被弹开,所以需要用isHurt来控制Movement是否执行(在update与Switchanim中用if语句控制)
anim.SetBool("hurt", true);
anim.SetFloat("running", 0);
}
//以下是我自己的方法
//else
//{
// hitSound.Play();
// player.velocity = new Vector2(-5.0f * facedirection, player.velocity.y);//弹走的方向与小狐狸面朝的方向相反
// isHurt = true;
// anim.SetBool("hurt", true);
// anim.SetFloat("running", 0);
//}
}
}
}
敌人父类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
protected Animator anim;
protected AudioSource death;
// Start is called before the first frame update
protected virtual void Start()//virtual代表虚拟的,相当于这个父级是可以被子级重新编写的
{
anim = GetComponent<Animator>();
death = GetComponent<AudioSource>();
}
public void Death()
{
death.Play();
anim.SetTrigger("death");
}
public void Jumpup()
{
Destroy(gameObject);
}
// Update is called once per frame
void Update()
{
}
}
敌人—青蛙(子类):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy_Frog : Enemy
{
private Rigidbody2D fg;
//private Animator anim;
public Transform left;//获取左侧物体的位置
public Transform right;//获取右侧物体的位置
private float leftx;//左侧物体的x坐标
private float rightx;//右侧物体的x坐标
public float speed,jumpForce;//设置速度(外部可更改)
private bool faceleft=true;//用来判断朝向是否向左
private Collider2D coll;
public LayerMask ground;
// Start is called before the first frame update
protected override void Start()
{
base.Start();
coll = GetComponent<Collider2D>();
//anim = GetComponent<Animator>();
fg = GetComponent<Rigidbody2D>();
//由于左右两个物体是敌人的子物体,所以如果敌人移动那么两个子物体也会跟着一起移动
//永远也到不了左侧物体的左边,敌人会一直向左移动,所以需要利用DetachChildren()方法断绝父子关系
transform.DetachChildren();
leftx = left.position.x;//获取左侧物体的位置
rightx = right.position.x;//获取右侧物体的位置
//断绝父子关系后子物体会在Hierarchy出现,当敌人多了之后就会有很多子物体出现在Hierarchy中,影响观感和性能
Destroy(left.gameObject);//所以在这里,我们直接将两个子物体销毁
Destroy(right.gameObject);
}
// Update is called once per frame
void Update()
{
Switch();
}
void Movement()
{
if (faceleft)//当面朝左侧的时候
{
if (coll.IsTouchingLayers(ground))
{
anim.SetBool("jumping", true);
fg.velocity = new Vector2(-speed, jumpForce);
}
//向左移动
if (transform.position.x < leftx)//当敌人的x坐标小于左侧的坐标时,青蛙朝向向右
{
transform.localScale = new Vector3(-1, 1, 1);
fg.velocity = new Vector2(speed, fg.velocity.y);//为了防止青蛙面朝右侧,却还向左跳的bug
faceleft = false;//向左的标志为false;
}
}
else
{
if (coll.IsTouchingLayers(ground))
{
anim.SetBool("jumping", true);
fg.velocity = new Vector2(speed, jumpForce);
}
if (transform.position.x > rightx)
{
transform.localScale = new Vector3(1, 1, 1);
fg.velocity = new Vector2(-speed, fg.velocity.y);
faceleft = true;
}
}
}
void Switch()
{
if (anim.GetBool("jumping")&&fg.velocity.y < 0)
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);//跳跃转换为下落
}
if (anim.GetBool("falling") && coll.IsTouchingLayers(ground))
{
anim.SetBool("falling", false);//下落转换成idle的条件
}
}
}
(想要获得全部项目资源或代码请在下方留言或私信)