Unity 2D -- Ruby Adventure 学习笔记

笔者刚刚接触unity,跟着B站上的教程学着做了一个小项目,若有不足,请各位大佬多多指教。

1. 创建项目

视频教程网址:
https://www.bilibili.com/video/BV1V4411W787?p=6&spm_id_from=pageDriver


项目预览

素材搜集:Window-general-Assert store
导入后可在assert文件夹中查看

2. 角色移动

  1. 新建文件夹 scripts脚本
  2. 创建脚本文件PlayerControl.cs
    可能出现的问题:unity没有补全功能
    在unity中设置,edit-preference 默认vs再打开
public class PlayerController : MonoBehaviour
{

    public float speed = 5f;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
        float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;

        Vector2 position = transform.position;
        position.x += moveX*speed*Time.deltaTime;
        position.y += moveY * speed * Time.deltaTime;
        transform.position = position;
    }
}
  1. 将脚本拖给人物

3. tilemap绘制地图

  1. 左侧窗口右键创建 Grid-tilemap
  2. 创建tile文件夹,里面可以放瓦片
  3. 裁剪瓦片 右侧spliteditor
    调整行与列,自动裁剪成n*m
  4. window-2D-tilepalette
    调色板中放入图片来作为地板

4. prefab丰富场景

  1. 将要加入的物体加入到prefab文件夹中,作为模板,方便复制粘贴
  2. 给物体添加刚体与碰撞
    给人物添加刚体(rigid body) 与碰撞(Box colider)
    给物体添加碰撞(Box Colider)
  3. 设置人物重力为零,刚体中gravity设置 (bug 1: 人物不停下降,根本停不下来)
  4. 更改 edit-project Setting-graphics
    调整mode为自定义(custom axis)
    x,y,z 0 1 0
    目的为图层的穿插更真实

4. 解决碰撞与抖动问题

  1. 解决碰撞旋转 (bug 2:爱的魔力转圈圈)
    人物-刚体-freeze rotation z
  2. 解决抖动问题: (bug 3: 不断抽搐)
public class PlayerController : MonoBehaviour
{

    public float speed = 5f;
    Rigidbody2D rBody;//刚体组件
    // Start is called before the first frame update
    void Start()
    {
        rBody=GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
        float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;

        Vector2 position = rBody.position;
        position.x += moveX*speed*Time.fixedDeltaTime;
        position.y += moveY * speed * Time.fixedDeltaTime;
        rBody.MovePosition(position);
    }
}

修改为刚体的位置,并且使用fixedDeltaTime替换DeltaTime

4.相机跟随

添加相机

import package cinemaMachine

GameObject-cinemaMachine-2dCamera

将人物添加到相机的follow中

地板碰撞

添加tilemapcollider
将除了水面以外 的瓦片 collidertype 都设为none

镜头边界

左侧创建新建空物体,取名为cameraConfirmer
为这个物体添加组件:Polygon Collider
编辑边界
在原相机中extension tools添加camera collider,把自己写的放进去。
confiner中点击istrigger,避免将人物弹出**(bug 4: 两者不兼容)**

采集道具:

  1. 图片调整合适大小拖入map,添加碰撞,打开触发器。
  2. 编辑脚本
    在playcontroller中添加
public class PlayerController : MonoBehaviour
{

  public float speed = 5f;
  private float MaxHealth = 5;
  private float MinHealth = 0;
  private float currHealth ;
  public float getMaxHealth()
  {
      return MaxHealth;
  }
  public float getMinHealth()
  {
      return MinHealth;
  }
  public float getCurrHealth()
  {
      return currHealth;
  }


  Rigidbody2D rBody;//刚体组件
  // Start is called before the first frame update
  void Start()
  {
      this.currHealth = 2;
      rBody=GetComponent<Rigidbody2D>();
  }

  // Update is called once per frame
  void Update()
  {
      float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
      float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;

      Vector2 position = rBody.position;
      position.x += moveX * speed * Time.fixedDeltaTime;
      position.y += moveY * speed * Time.fixedDeltaTime;
      rBody.MovePosition(position);
  }
  public void changeHealth(float add)
  {
      currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
  }
  }

新建脚本 collectable.cs

    private void OnTriggerEnter2D(Collider2D other)
    {
        PlayerController player = other.GetComponent<PlayerController>(); 
        if (player != null)
        {
            Debug.Log("玩家碰到草莓");
            Debug.Log(player.getCurrHealth() + "/" + 5);
            if (player.getCurrHealth() < player.getMaxHealth())
            {

                player.changeHealth(2);
                Debug.Log(player.getCurrHealth() + "/" + 5);
                Destroy(this.gameObject);//拾取草莓后摧毁草莓
 
            }
            
        }

    }

受到伤害

  1. 创建脚本,类似于collectable
private void OnTriggerStay2D(Collider2D other)
    {
        PlayerController player = other.GetComponent<PlayerController>();
        if (player != null)
        {
            Debug.Log("玩家碰到陷阱");
            Debug.Log(player.getCurrHealth() + "/" + 5);
            if (player.getCurrHealth() > 0)
            {

                player.changeHealth(-1);
                Debug.Log(player.getCurrHealth() + "/" + 5);
                


            }


        }

    }//注意方法为OnTriggerStay2D,可实现持续收到伤害
  1. 在Playercontroller中改动实现间隔受到伤害
    private float invincibleTimer;
    private float invincibleTime=2f ;
    private bool isinvincible=false;
    //
    if (isinvincible)
        {
            invincibleTimer -= Time.deltaTime;
            if(invincibleTimer < 0)
                isinvincible=false;//两秒以后取消无敌状态
        }
    //在update函数中
    public void changeHealth(float add)
    {
        if(add<0)
        {
            if (isinvincible)
            {
                return;

            }
            isinvincible = true;
            invincibleTimer = invincibleTime;
        }

        currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
    }
    //修改changeHealth函数
  1. 实现人物站在陷阱里也能收到伤害
    人物刚体选择neverawake

  2. 伤害区域的扩充
    选中伤害图片-drawmode-tiled平铺

添加敌人

1,给敌人加上刚体与碰撞,重力设为0;
2,编辑脚本EnemyController

public class EnemyController : MonoBehaviour
{
    public float changeDirectionTime = 2f;//改变方向的时间
    public float changeTimer;//改变方向的计时器
    public float speed = 3f;
    public bool isVertical;//是否垂直方向移动
    Rigidbody2D rbody;
    private Vector2 moveDirection;//移动方向   
    // Start is called before the first frame update
    void Start()
    {
        
        rbody= GetComponent<Rigidbody2D>();
        moveDirection=isVertical ? Vector2.up : Vector2.right;//如果垂直移动,向上,否则向右
        changeTimer = changeDirectionTime;
    }

    // Update is called once per frame
    void Update()
    {
        changeTimer -= Time.deltaTime;
        if (changeTimer < 0)
        {
            moveDirection *= -1;
            changeTimer = changeDirectionTime;
        }//计时器使来回运动

        Vector2 position=rbody.position;
        position.x+= moveDirection.x*Time.deltaTime;
        position.y += moveDirection.y * Time.deltaTime;
        rbody.MovePosition(position);
    }
    /// 
    /// 与玩家的碰撞检测
    /// 
    /// 
    private void OnCollisionEnter2D(Collision2D collision)//碰撞体检测,不会穿过,要求两刚体之间
    {
        PlayerController pc = collision.gameObject.GetComponent<PlayerController>();
        if (pc != null)
        {
            pc.changeHealth(-1);
        }
    }
}

添加动画(难点!!!)

物品动画

  1. 选中需要添加动画的物体, 点击windows-animation 新建一个动画 新建在item文件夹下创建
  2. add Property – scale 控制缩放 在每一帧设置不同的大小
  3. 另外所有要加动画的物体点击修改过的物体 prefebs-apply all

敌人动画

  1. 选中预支动作图片,将其批量选中walkdown拖动到robot下,会打开资源管理器,选择新建enemy文件夹存放,保存为walkdown.anim 。
  2. 分别如此创建向上下左右的动画,特例右和左是对称的,所以在向右animation中 add property – sprite renderer.FlipX,在x轴上旋转。
    之后再添加fixed动画
  3. 动画的应用,创建混合树,在animator界面-右键-create state-new blend tree
    blendType:2D Simple
    下方加号:add motion field,把上下左右的动作拖入,分别设置positionX,Y的变化(初始化为moveX,moveY)
    返回base layer 使entry指向blend tree
    左侧参数添加 moveX,moveY 以及trigger型的fixed,利用右键blend tree实现transition的连接,点击这根转换线,将transition的参数设置为trigger型的变量fixed
  4. 修改动画代码:
public class EnemyController : MonoBehaviour
{
   public float changeDirectionTime = 2f;//改变方向的时间
   public float changeTimer;//改变方向的计时器
   public float speed = 3f;
   public bool isVertical;//是否垂直方向移动
   Rigidbody2D rbody;
   private Vector2 moveDirection;//移动方向   
   // Start is called before the first frame update

   private Animator animator;

   void Start()
   {
       
       rbody= GetComponent<Rigidbody2D>();

       animator= GetComponent<Animator>();

       moveDirection=isVertical ? Vector2.up : Vector2.right;//如果垂直移动,向上,否则向右
      
       changeTimer = changeDirectionTime;
   }

   // Update is called once per frame
   void Update()
   {
       changeTimer -= Time.deltaTime;
       if (changeTimer < 0)
       {
           moveDirection *= -1;
           changeTimer = changeDirectionTime;
       }//计时器使来回运动

       Vector2 position=rbody.position;
       position.x+= moveDirection.x*Time.deltaTime;
       position.y += moveDirection.y * Time.deltaTime;
       rbody.MovePosition(position);
       animator.SetFloat("moveX",moveDirection.x);
       animator.SetFloat("moveY", moveDirection.y);
   }
   /// 
   /// 与玩家的碰撞检测
   /// 
   /// 
   private void OnCollisionEnter2D(Collision2D collision)//碰撞体检测,不会穿过,要求两刚体之间
   {
       PlayerController pc = collision.gameObject.GetComponent<PlayerController>();
       if (pc != null)
       {
           pc.changeHealth(-1);
       }
   }
}

人物动画

  1. 人物组件添加animator,添加控制 Ruby(官方自带,也可以用Blend tree自行完成)
  2. 修改代码:
public class PlayerController : MonoBehaviour
{

    public float speed = 5f;
    private float MaxHealth = 5;
    private float MinHealth = 0;
    private float currHealth ;
    private float invincibleTimer;
    private float invincibleTime=2f ;
    private bool isinvincible=false;
    private Animator animator;//动画机
    private Vector2 lookDirection = new Vector2(1, 0);//初始朝向默认为向右
    public float getMaxHealth()
    {
        return MaxHealth;
    }
    public float getMinHealth()
    {
        return MinHealth;
    }
    public float getCurrHealth()
    {
        return currHealth;
    }


    Rigidbody2D rBody;//刚体组件
    // Start is called before the first frame update
    void Start()
    {
        this.currHealth = 2;
        invincibleTimer = 0;
        rBody=GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
        float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;

        //获取朝向:
        Vector2 moveVector= new Vector2(moveX, moveY);
        if(moveX!=0 || moveY != 0)//至少一个不为零(按下了键盘)
        {
            lookDirection = moveVector;
        }


        animator.SetFloat("Look X",moveVector.x);
        animator.SetFloat("Look Y", moveVector.y);
        animator.SetFloat("Speed", moveVector.magnitude);//向量的模



        //控制移动
        Vector2 position = rBody.position;
        //position.x += moveX*speed*Time.fixedDeltaTime;
        //position.y += moveY * speed * Time.fixedDeltaTime;

        position += moveVector*speed*Time.deltaTime;

        rBody.MovePosition(position);


        //控制无敌时间
        if (isinvincible)
        {
            invincibleTimer -= Time.deltaTime;
            if(invincibleTimer < 0)
                isinvincible=false;//两秒以后取消无敌状态
        }

    }
    public void changeHealth(float add)
    {
        if(add<0)
        {
            if (isinvincible)
            {
                return;

            }
            isinvincible = true;
            invincibleTimer = invincibleTime;
        }

        currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
        Debug.Log("当前生命值" + currHealth);
    }
}

添加子弹

  1. 将子弹拖入场景,加上刚体与碰撞,重力设为零,不加z旋转,不勾选 isTrigger

  2. 添加脚本 bulletController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletController : MonoBehaviour
{
    private Rigidbody2D rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();//优先于start
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void Move(Vector2 moveDirection, float moveForce)
        //发射方法
    {
        rb.AddForce(moveDirection*moveForce);
    }
}
  1. 修改图层与碰撞
    右上角layer图层添加 player与bullet层
    edit-preference-2D physical 修改碰撞:player与bullet之间,bullet与bullet之间禁止碰撞

添加enemy逻辑

public void Fixed()
    {
        rbody.simulated = false;//禁用物理模拟
        animator.SetTrigger("fixed");//播放被修复的动画
        isfixed = true;
    }

plyercontroller 添加发射逻辑与动画


if (Input.GetKeyDown(KeyCode.J))
        {
            animator.SetTrigger("Launch");
            GameObject bullet = Instantiate(bulletPrefab, rBody.position+Vector2.up *0.4f ,Quaternion.identity);
            //默认方向,四元数,参数分别为预制体类别,产生出来的位置,旋转的初始角度
            BulletController bc=bullet.GetComponent<BulletController>();
            if (bc != null)
            {
                bc.Move(lookDirection, 300);
            }
        }

添加特效:可以自学粒子相关

烟雾特效

  1. 右键左侧物品栏,新建特效particle System

https://www.bilibili.com/video/BV1V4411W787?p=15

人物血条

  1. 添加ui

右侧列表-右键新建ui-image
将要添加的图片加进去
调整缩放模式convas-scale with screen size

调整血条:filed, horizontal

  1. 编辑ui事件,在血量变化同时ui界面血量条随即变化

Unity 2D -- Ruby Adventure 学习笔记_第1张图片

npc对话框,相应事件

子弹数量ui

Unity 2D -- Ruby Adventure 学习笔记_第2张图片

链接如下,需要补充。
B站教程

本项目github网址

感谢大家阅读!!!

你可能感兴趣的:(Unity2D,unity,ruby,游戏引擎)