unity 2D 学习笔记(1)

学习视频教程 无邪:
https://space.bilibili.com/335835274/channel/detail?cid=112338
 

 

 

画个自己的像素人物
后续是给这个人物添加动画
 

 

 

直接将导出的png图片拖入Unity中,创建一个2d对象
把每个导入的动作分割成单独的16*16的图片,
多选中多张图片一起拖到对象上,则可給该对象创建动画
 
 

 

 

打开动画窗口 即可看到当前对象的所有动画
在动画界面中创建 几个全局变量来控制动画切换
在脚本代码中 获取该对象的动画组件 并通过判断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);
最终还是决定做个横板跳跃游戏
人物绘制如下
 

 

 

一个会跳跃的小至尊宝,名字叫做Hx
 
 
 
 

 

 

做一个简易场景,给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);
                }
            }
        }

    }

}

 

添加攻击动画
理论上来说攻击可以从任意状态触发,则动画流程图如下
 

 

 

攻击状态可以从any state出现,动画结束后又可以回到之前的状态
攻击动画选用Triger类型的变量触发
触发代码为
    private void Acttack()
    {
        if (Input.GetButtonDown("Attack"))
        {
            myAnimator.SetTrigger("IsHxAttack");
            Debug.Log("IsHxAttack");
        }
    }

 

 
攻击状态下添加HitBox
 

 

 

在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;
    }

 

 
攻击效果实现
创建一个物体,这里我创建了一截树干
 

 

 

 
给树干添加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);
        }
    }

 

 
若Hx的攻击范围 进入了某个Collider内,则函数内进行判断 碰撞到的Collider是否属于Enemy标签的
如果属于 则调用该Enemy的TaskDamage()函数 即攻击完成;

 
瓦片绘制功能
在Aseprinte中绘制64*32的图像,单个32*32为一个基本单元
绘制两块地面并导出图像至Unity切割;
完毕后 在场景中创建TitleMap对象,则在Grid中存在TitleMap对象,打开Title Palette窗口,将切割好的模块拖进去,即可在场景中绘制
 

 

 

 
绘制完成后需要给场景里面的TitleMap对象添加碰撞体 TitleMap Collider 2d
并添加Composite Collider 2d组件,添加这个碰撞组件之后会自动带一个Rigibody组件,将Rigibody组件中BodyType设置为Static,表明
这个对象是一个静态的对象,不受重力作用;
 
这些设置完毕之后 开启工程,发现在该TitleMap移动的时候 Y方向上有个很小的速度 在正负变换,我是通过Y方向上速度的正负来判断人物是
在跳起来上升阶段 还是处于下落阶段,并更改不同的动画,所以这时候判断就不能直接大于零或者小于零,应该大于0.1或者小于-0.1;
 
 

 

 

 
地图绘制完毕后,发现角色可以爬墙,也就是在竖直的碰撞体上,只要你一直向着墙的方向移动,则不会掉下来,此时Y方向上的速度也为0,跳跃的条件我设置的是
Y方向上速度大于0.1或者小于-0.1,此时Y方向上速度为0,则可以再次跳跃,此时就达到的爬墙的效果,虽然是一个BUG 不过准备保留,用于进入一些隐藏的关卡;
 
绘制了初始场景的地图 如下:
 
计划这里为一个选择关卡的场景
左边第一位置为攀大场景,后续根据情况再添加;
在中间靠上的位置 设置了一个牛头随机移动,后续准备做成 在平台上击打一个对象,则开启牛头的移动,没有什么作用,隐藏彩蛋。
 
 
 
 
 
 
 
 
 
posted @ 2022-03-18 23:14  黄晓魚  阅读(164)  评论(0编辑  收藏  举报