unity 2D 学习笔记(1)
学习视频教程 无邪:
https://space.bilibili.com/335835274/channel/detail?cid=112338
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230613778-998276447.png)
画个自己的像素人物
后续是给这个人物添加动画
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230659418-171654970.png)
直接将导出的png图片拖入Unity中,创建一个2d对象
把每个导入的动作分割成单独的16*16的图片,
多选中多张图片一起拖到对象上,则可給该对象创建动画
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230724069-1832894387.png)
打开动画窗口 即可看到当前对象的所有动画
在动画界面中创建 几个全局变量来控制动画切换
在脚本代码中 获取该对象的动画组件 并通过判断player的动作 方向 来设置几个全局变量的值,理论上这几个全局变量一旦切换 动画就会根据逻辑对应的切换
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public float MoveSpeed; private Animator Myanimator; private Rigidbody2D Myrigidbody2D; // Start is called before the first frame update void Start() { MoveSpeed = 5; Myanimator = GetComponent<Animator>(); Myrigidbody2D = GetComponent<Rigidbody2D>(); } // Update is called once per frame void Update() { PlayerMove(); CheckPlayerDirectory(); } void PlayerMove() { float MoveDirH = Input.GetAxis("Horizontal"); float MoveDirV = Input.GetAxis("Vertical"); Vector2 vector = new Vector2(MoveDirH * MoveSpeed, MoveDirV* MoveSpeed); Myrigidbody2D.velocity = vector; //if (Input.GetKey(KeyCode.W)) //{ // gameObject.transform.Translate(Vector3.up * MoveSpeed * Time.deltaTime); //} //if (Input.GetKey(KeyCode.S)) //{ // gameObject.transform.Translate(Vector3.down * MoveSpeed * Time.deltaTime); //} //if (Input.GetKey(KeyCode.A)) //{ // gameObject.transform.Translate(Vector3.left * MoveSpeed * Time.deltaTime); //} //if (Input.GetKey(KeyCode.D)) //{ // gameObject.transform.Translate(Vector3.right * MoveSpeed * Time.deltaTime); //} } void CheckPlayerDirectory() { if (Myrigidbody2D.velocity.x < 0) { Myanimator.SetBool("IsMoveLeft", true); Debug.Log("IsMoveLeft -> true"); } else { Myanimator.SetBool("IsMoveLeft", false); } /**************************************************************/ if (Myrigidbody2D.velocity.x > 0) { Myanimator.SetBool("IsMoveRight", true); } else { Myanimator.SetBool("IsMoveRight", false); } /**************************************************************/ if (Myrigidbody2D.velocity.y > 0) { Myanimator.SetBool("IsMoveUp", true); } else { Myanimator.SetBool("IsMoveUp", false); } /**************************************************************/ if (Myrigidbody2D.velocity.y < 0) { Myanimator.SetBool("IsMoveDown", true); } else { Myanimator.SetBool("IsMoveDown", false); } /**************************************************************/ if (Mathf.Abs( Myrigidbody2D.velocity.y ) < Mathf.Epsilon && Mathf.Abs(Myrigidbody2D.velocity.x) < Mathf.Epsilon) { Myanimator.SetBool("IsMoveDown", true); } }
向左走和向右走 可以共享同一个动画
具体做法是判断 角色的运动方向 并在脚本里面选择是否反转角色
代码如下
transform.localRotation = Quaternion.Euler(0,180,0);
最终还是决定做个横板跳跃游戏
人物绘制如下
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230825479-1528929857.png)
一个会跳跃的小至尊宝,名字叫做Hx
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230835428-257566061.png)
做一个简易场景,给Hx的脚部添加Box Collider 2d组件 使得其能站在地面上
给Hx的身体部分添加Capsule Collider 2d 使其能和其他物体产生碰撞
添加脚本控制人物移动 和 判断人物当前运动状态 切换不同的动画
代码如下
using System.Collections; using System.Collections.Generic; using UnityEngine; public class HxControl : MonoBehaviour { public float MoveSpeed; public bool IsAdministrator; private Rigidbody2D myrigibody; private Animator myAnimator; // Start is called before the first frame update void Start() { MoveSpeed = 5; myrigibody = GetComponent<Rigidbody2D>(); myAnimator = GetComponent<Animator>(); //IsAdministrator = false; } // Update is called once per frame void Update() { Run(); Jump(); CheckAnimotion(); } private void Run() { float MoveDir = Input.GetAxis("Horizontal"); Vector2 vector2 = new Vector2(MoveDir*MoveSpeed,myrigibody.velocity.y); myrigibody.velocity = vector2; } private void Jump() { if (myAnimator.GetBool("IsHxJump") || myAnimator.GetBool("IsHxFly")) { } else { if (Input.GetButtonDown("Jump")) { Vector2 vector2 = new Vector2(myrigibody.velocity.x, MoveSpeed * 1.4f); myrigibody.velocity = vector2; } } //确认为超级管理员 则可以随便飞 if (IsAdministrator) { if (Input.GetButtonDown("Jump")) { Vector2 vector2 = new Vector2(myrigibody.velocity.x, MoveSpeed * 1.4f); myrigibody.velocity = vector2; } } } private void CheckAnimotion() { if (Mathf.Abs(myrigibody.velocity.x) < Mathf.Epsilon && Mathf.Abs(myrigibody.velocity.y) < Mathf.Epsilon) { myAnimator.SetBool("IsHxJump", false); myAnimator.SetBool("IsHxRun", false); myAnimator.SetBool("IsHxFly", false); } else { if (Mathf.Abs(myrigibody.velocity.y) > Mathf.Abs(Mathf.Epsilon)) { if (myrigibody.velocity.y > Mathf.Epsilon) { myAnimator.SetBool("IsHxJump", true); } if (myrigibody.velocity.y < -Mathf.Epsilon) { myAnimator.SetBool("IsHxFly", true); } } else { myAnimator.SetBool("IsHxFly", false); myAnimator.SetBool("IsHxJump", false); if (myrigibody.velocity.x < -Mathf.Epsilon) { myAnimator.SetBool("IsHxRun", true); transform.localRotation = Quaternion.Euler(0, 0, 0); } if (myrigibody.velocity.x > -Mathf.Epsilon) { myAnimator.SetBool("IsHxRun", true); transform.localRotation = Quaternion.Euler(0, 180, 0); } } } } }
添加攻击动画
理论上来说攻击可以从任意状态触发,则动画流程图如下
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230905005-816578138.png)
攻击状态可以从any state出现,动画结束后又可以回到之前的状态
攻击动画选用Triger类型的变量触发
触发代码为
private void Acttack() { if (Input.GetButtonDown("Attack")) { myAnimator.SetTrigger("IsHxAttack"); Debug.Log("IsHxAttack"); } }
攻击状态下添加HitBox
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318230933179-175033581.png)
在Hx下新建一个空的子对象 名字叫HxAttack
在HxAttack下面 添加组件 Polygon Colloder 2d 多边形碰撞组件 取消该组件前的复选框
添加脚本:判断当攻击按键按下时 使能碰撞组件,并一定时间后禁用碰撞组件
脚本代码如下:
public int DamdgeVal; private Animator animator; private PolygonCollider2D polygonCollider; // Start is called before the first frame update void Start() { animator = GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>(); polygonCollider = GetComponent<PolygonCollider2D>(); } // Update is called once per frame void Update() { } void Attack() { if (Input.GetKeyDown("Attack")) { polygonCollider.enabled = true; StartCoroutine(DisableHitBox()); } } IEnumerator DisableHitBox() { yield return new WaitForSeconds(0.6f); polygonCollider.enabled = false; }
攻击效果实现
创建一个物体,这里我创建了一截树干
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318231008319-1646402476.png)
给树干添加Box Collider 2d 碰撞检测框
/// <summary> /// 怪物的血量 /// </summary> public int healthVal; /// <summary> /// 怪物的伤害值 /// </summary> public int damageVal; /// <summary> /// /// </summary> private SpriteRenderer sr; /// <summary> /// the base Color of The Enemy /// </summary> private Color OriginColor;
脚本代码中 添加以上变量
public void Start() { sr = GetComponent<SpriteRenderer>(); OriginColor = sr.color; }
获取树干的精灵组件,将原始颜色赋值给变量
public void Update() { if (healthVal <= 0) { Destroy(gameObject); } }
每一帧判断自身血量,若血量归零 则摧毁这个对象
public void TaskDamage(int damage) { this.healthVal -= damage; Instantiate(BloodEffect,transform.position,Quaternion.identity); FlashColor(0.3f); } void FlashColor(float conTime) { sr.color = Color.red; Invoke("ResetColor", conTime); } void ResetColor() { sr.color = OriginColor; }
新增public 受到攻击的函数 供外部对象调用
受到攻击后自身生命值减少/
初始化一个外部传进来的GameObject(这是外面写好的一个粒子特效,用作掉血效果)/
自身的颜色切换成Color.Red并延时0.3s后恢复原来的颜色
而在Hx代码块内 新增攻击函数如下:
private void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Enemy")) { other.GetComponent<Enemy>().TaskDamage(DamdgeVal); } }
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318231319621-851128182.png)
若Hx的攻击范围 进入了某个Collider内,则函数内进行判断 碰撞到的Collider是否属于Enemy标签的
如果属于 则调用该Enemy的TaskDamage()函数 即攻击完成;
瓦片绘制功能
在Aseprinte中绘制64*32的图像,单个32*32为一个基本单元
绘制两块地面并导出图像至Unity切割;
完毕后 在场景中创建TitleMap对象,则在Grid中存在TitleMap对象,打开Title Palette窗口,将切割好的模块拖进去,即可在场景中绘制
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318231339869-2111769783.png)
绘制完成后需要给场景里面的TitleMap对象添加碰撞体 TitleMap Collider 2d
并添加Composite Collider 2d组件,添加这个碰撞组件之后会自动带一个Rigibody组件,将Rigibody组件中BodyType设置为Static,表明
这个对象是一个静态的对象,不受重力作用;
这些设置完毕之后 开启工程,发现在该TitleMap移动的时候 Y方向上有个很小的速度 在正负变换,我是通过Y方向上速度的正负来判断人物是
在跳起来上升阶段 还是处于下落阶段,并更改不同的动画,所以这时候判断就不能直接大于零或者小于零,应该大于0.1或者小于-0.1;
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318231405198-1347946607.png)
地图绘制完毕后,发现角色可以爬墙,也就是在竖直的碰撞体上,只要你一直向着墙的方向移动,则不会掉下来,此时Y方向上的速度也为0,跳跃的条件我设置的是
Y方向上速度大于0.1或者小于-0.1,此时Y方向上速度为0,则可以再次跳跃,此时就达到的爬墙的效果,虽然是一个BUG 不过准备保留,用于进入一些隐藏的关卡;
绘制了初始场景的地图 如下:
![](https://img2022.cnblogs.com/blog/1579956/202203/1579956-20220318231411363-91262895.png)
计划这里为一个选择关卡的场景
左边第一位置为攀大场景,后续根据情况再添加;
在中间靠上的位置 设置了一个牛头随机移动,后续准备做成 在平台上击打一个对象,则开启牛头的移动,没有什么作用,隐藏彩蛋。