当前位置:   article > 正文

Unity Sunny Land开发流程(一)

unity sunny land

0.写在前面

  此篇博客用来记录游戏开发过程中遇到的各种问题以及大致的流程,方便自己以后回顾学习,如果想要了解完整的开发过程,最好去看教学视频。
   U n i t y Unity Unity版本: 2019.4.12 f 1 2019.4.12f1 2019.4.12f1
  相应的视频教程请在 B B B站搜索 u p up up M _ S t u d i o M\_Studio M_Studio观看。

1.导入素材

  在商店中搜索 S u n n y   L a n d Sunny\ Land Sunny Land即可找到对应的资源,下载后导入到当前项目即可。
在这里插入图片描述

2.编辑素材&Tilemap

  在编辑背景等图片资源之前有一个知识点需要我们了解,那就是单位格:
在这里插入图片描述
  上图所示的每一个小方格都是单位格,而图片资源会有如下属性:
在这里插入图片描述
  通过修改这一参数(单位长度内的像素个数),我们就可以控制图片资源显示的大小。该项目内统一使用 16 16 16
  下面简单介绍一下 T i l e m a p Tilemap Tilemap是什么,怎么用。我们可以先看一张图片:
在这里插入图片描述
  简单来说,利用 T i l e m a p Tilemap Tilemap我们可以快速的创建 2 D 2D 2D地图。通过对 S p r i t e Sprite Sprite也就是图片的切分,我们可以得到一个个 T i l e Tile Tile,他们就相当于颜色;我们可以把它们附着在 P a l e t t e Palette Palette上面(调色板);然后通过 B r u s h Brush Brush T i l e m a p Tilemap Tilemap也就是场景中绘制,就像画画一样。那么在 u n i t y unity unity中整个流程是怎么样的呢?
   1. 1. 1.切分图片。
在这里插入图片描述
   2. 2. 2.创建 P a l e t t e Palette Palette
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

   3. 3. 3. T i l e Tile Tile添加到 P a l e t t e Palette Palette中(拖入图片创建 T i l e Tile Tile)。
在这里插入图片描述
   4. 4. 4.在场景中创建 T i l e m a p Tilemap Tilemap
在这里插入图片描述

   5. 5. 5.通过 B r u s h Brush Brush T i l e Tile Tile绘制到 T i l e m a p Tilemap Tilemap中。
在这里插入图片描述
  如果在 g a m e game game视图下看到场景中有缝隙,可以修改这个参数:
在这里插入图片描述

3.图层layer&角色建立

  有时候我们需要控制场景中不同物体的渲染顺序,比如地形之类的应该在背景之上显示,这个该怎么做呢? S o r t i n g   L a y e r 、 O r d e r   i n   L a y e r Sorting\ Layer、Order\ in\ Layer Sorting LayerOrder in Layer。前者选中的 l a y e r layer layer越靠下,显示的越靠上;同一 l a y e r layer layer中,后者的值越大,显示的越靠上。
在这里插入图片描述
  想要创建人物,首先要在场景中创建一个精灵,然后修改它所引用的图片。同时不要忘了修改位置、层级以及图片的大小( P i x e l s   p e r   u n i t Pixels\ per\ unit Pixels per unit)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  但是此时我们的人物还不会动,也不受重力影响。所以我们需要为其加上 r i g i d b o d y 2 D rigidbody2D rigidbody2D和碰撞体组件,当然,场景中的地形也需要加上碰撞体。

4.角色移动

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

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb2d;

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

    // Update is called once per frame
    void Update()
    {
        Movement();
    }
    
    void Movement()
    {
        float horizontalMove = Input.GetAxis("Horizontal");
        rb2d.velocity = new Vector2(horizontalMove * speed, rb2d.velocity.y);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

  上述代码即可实现简单的人物移动,但是在测试中你可能会发现人物会出现摔倒等现象,这是由于发生碰撞时人物绕 z z z轴旋转了,所以我们可以冻结这一旋转:
在这里插入图片描述

5.角色方向&跳跃

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

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb2d;

    public float speed = 5f;
    public float jumpForce = 5f;
    // Start is called before the first frame update
    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        Movement();
    }
    
    void Movement()
    {
        float horizontalMove = Input.GetAxis("Horizontal");
        int faceDirection = (int)Input.GetAxisRaw("Horizontal");
        rb2d.velocity = new Vector2(horizontalMove * speed, rb2d.velocity.y);
        if (faceDirection != 0)
        {
            transform.localScale = new Vector3(faceDirection, 1, 1);
        }
        if (Input.GetButtonDown("Jump"))
        {
            rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

  上述代码实现了人物的翻转、跳跃。翻转的做法有很多种,除了修改 s c a l e scale scale还可以通过修改这面这个属性做到:
在这里插入图片描述
  但是这两种方法不是一直都等价的,考虑人物的背上还有装备的情况,那么一般情况下它们是父子关系,此时修改父物体的 s c a l e scale scale,子物体也会一同修改,但是上面这个 F l i p Flip Flip是独立的,所以这种情况下想要实现翻转的话应该利用 s c a l e scale scale
  由于我们还没有做地面检测,所以人物可以一直跳。

6.动画效果Animation

  给人物添加 A n i m a t o r Animator Animator组件之后就可以开始制作动画了~

在这里插入图片描述
  通过这个设置可以查看当前动画的帧率并进行设置。
在这里插入图片描述
  按住左键不松可以选取一定区域内的所有关键帧,或者先选中一个关键帧,此时按住 s h i f t shift shift不松再选择另一个关键帧,即可自动选中它们之间的所有关键帧( u n i t y unity unity中选择多个文件的方法同理),此时再拖动(选中区域的两侧有竖线标识)即可均匀的设置间隔而无需一个一个的调整。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  在制作好 i d l e idle idle r u n run run两段动画后,就可以开始修改 a n i m a t o r animator animator了。我们需要为这两段动画之间加一个过渡条件:
在这里插入图片描述
在这里插入图片描述
  若勾选 H a s   E x i t   T i m e Has\ Exit\ Time Has Exit Time,则代表从动画 A A A到动画 B B B有特定的退出(转换)时间,具体值由 E x i t   T i m e Exit\ Time Exit Time决定,也就是说切换可能会有延迟(不能立即切换); T r a n s i t i o n   D u r a t i o n Transition\ Duration Transition Duration决定了过渡的时间,即动作 A A A经过多久可以过渡到动作 B B B。这里,我们既不需要退出时间,也不需要过渡时间。代码只需要改动一点点,修改 r u n n i n g running running的值即可。

void Movement()
{	
	animator.SetFloat("running", Mathf.Abs(horizontalMove));
}
  • 1
  • 2
  • 3
  • 4

7.跳跃动画 LayerMask

  跳跃动画需要分成两部分: 1. 1. 1.起跳; 2. 2. 2.下落,所以我们需要制作两个动画,然后修改状态机:
在这里插入图片描述

  现在考虑代码怎么改。起跳是很简单的,只需要在用户按下空格键时,将 j u m p i n g jumping jumping设为真;降落也比较简单,当人物在跳跃状态且 y y y方向的速度 < 0 <0 <0时,说明人物开始降落了,那么此时应将 j u m p i n g jumping jumping设为假, f a l l i n g falling falling设为真;关键是如何判断人物落地了呢?通过碰撞来判断。在这里我们引入 L a y e r M a s k LayerMask LayerMask,它在射线检测中经常被用到,用来实现与特定层的检测(忽略其它层)。那么我们自然想到可以将地面的 l a y e r layer layer设置为 g r o u n d ground ground,然后在代码中判断人物的碰撞体是否与 g r o u n d ground ground接触即可:
在这里插入图片描述

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

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb2d;
    private Animator animator;
    private BoxCollider2D boxCollider2D;

    public float speed = 5f;
    public float jumpForce = 5f;
    public LayerMask ground;
    // Start is called before the first frame update
    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        boxCollider2D = GetComponent<BoxCollider2D>();
    }

    // Update is called once per frame
    void Update()
    {
        Movement();
        SwitchAnimation();
    }
  
    void Movement()
    {
        float horizontalMove = Input.GetAxis("Horizontal");
        int faceDirection = (int)Input.GetAxisRaw("Horizontal");
        rb2d.velocity = new Vector2(horizontalMove * speed, rb2d.velocity.y);
        animator.SetFloat("running", Mathf.Abs(horizontalMove));
        if (faceDirection != 0)
        {
            transform.localScale = new Vector3(faceDirection, 1, 1);
        }
        if (Input.GetButtonDown("Jump"))
        {
            rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);
            animator.SetBool("jumping", true);
        }
    }

    void SwitchAnimation()
    {
        //animator.SetBool("idle", false);
        if (animator.GetBool("jumping")) //跳跃状态
        {
            if (rb2d.velocity.y < 0)
            {
                animator.SetBool("jumping", false);
                animator.SetBool("falling", true);
            }
        }
        else if (animator.GetBool("falling")) //下落状态
        {
            if (boxCollider2D.IsTouchingLayers(ground))
            {
                animator.SetBool("falling", false);
                animator.SetBool("idle", true);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

  上述代码实现了人物各种动作之间的切换。你可能注意到了上述代码中的一些问题,当 i d l e idle idle被设为真后,就一直为真,但是这并不影响动作的切换,因为到目前为止 i d l e idle idle其实没有存在的必要,但是为了与教程统一,我还是加入了这个变量。所以大家不要盲目的相信教程全都是正确的,从教程中学到方法,然后自己思考怎么做。

8.修复移动错误&Body Type&Composite Collider

  人物在移动时可能出现卡住不动的情况,这个应该是碰撞体的问题。在 s c e n e scene scene视图下我们可以看到当前地形( T i l e m a p Tilemap Tilemap)的碰撞体:
在这里插入图片描述
  可以发现每一个方格都有自己的碰撞体,然而很多地方是没有必要的,比如地形的内部。所以我们需要使用 c o m p o s i t e   c o l l i d e r composite\ collider composite collider来把这些碰撞体合并到一起,具体做法就是为 t i l e m a p tilemap tilemap添加一个对应的组件:
在这里插入图片描述

在这里插入图片描述
  可以看到地形内部的碰撞体消失了(被合并成了一个整体),这样做不仅可以减少计算量,提高性能,还可以解决人物移动时突然卡住的 b u g bug bug。但是如果这个时候运行游戏,你会发现地形和人物一起掉落了,这是因为 c o m p o s i t e   c o l l i d e r   2 D composite\ collider\ 2D composite collider 2D需要 r i g i d b o d y   2 D rigidbody\ 2D rigidbody 2D,所以地形也会受到重力影响,这个问题怎么解决呢?首先我们来了解一下 r i g i d b o d y   2 D rigidbody\ 2D rigidbody 2D b o d y   t y p e body\ type body type
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
  显然,我们是不希望地形进行移动的,所以应该选取 s t a t i c static static

9.镜头控制Cinemachine

   C i n e m a c h i n e Cinemachine Cinemachine是一个非常棒的插件,利用它可以快速制作出满足我们需求的摄像机——跟随人物缓慢移动。首先我们需要把背景拉长,多复制几份即可:
在这里插入图片描述
  注意这里有一个小技巧,在利用移动工具移动游戏对象时,按住 V V V会在鼠标附近的区域出现如上图所示的白色方框,拖动白色方框移动游戏对象则会使它附着在附近的其它游戏对象上,也就是说可以自动进行对齐等操作,更加方便。
  修改完背景后,就可以添加 C i n e m a c h i n e Cinemachine Cinemachine摄像机了,并且让它跟随我们的人物。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  从上图可以看到这个摄像机分为三个区域,人物在最中间的区域移动时,摄像机不会跟随;在浅蓝色区域移动时,摄像机缓慢跟随;在红色区域移动时,摄像机快速跟随。右边红框内的参数可以调节这几个区域的大小。现在还有一个问题就是,当相机随着人物移动到边界时,会看到超出背景的部分,这是我们不希望的。但是这个摄像机已经为我们考虑到了:
在这里插入图片描述
在这里插入图片描述
  我们只需要给背景增加一个 p o l y g o n   c o l l i d e r   2 D polygon\ collider\ 2D polygon collider 2D(编辑这种碰撞体时,按住 c t r l ctrl ctrl可以删除某一个点),然后把它拖到上面的 B o u n d i n g Bounding Bounding即可。注意把背景的碰撞体设置成触发器,不然会把人物顶出去。

10.物品收集 & Perfabs

  仿照人物的制作方式,自己动手制作樱桃,并修改其 t a g tag tag
在这里插入图片描述
在这里插入图片描述

  修改代码使得人物碰到樱桃时,樱桃消失(之前写的代码省略掉了):

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

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private int cherry = 0;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Collection")
        {
            Destroy(collision.gameObject);
            ++cherry;
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  新建 P r e f a b Prefab Prefab文件夹,把人物和樱桃都设置为预制体:
在这里插入图片描述

  接下来大家可以自己发挥,把场景设置的更加丰富~

11.物理材质&空中跳跃

  相信大家在游玩的过程中可能已经发现了一些问题: 1. 1. 1.人物碰到某个物体后如果一直按着方向键,那么人物不会落下,会卡在物体上; 2. 2. 2.人物可以无限跳跃。现在我们就来解决这些问题。
  首先来解决第一个问题,为什么按着方向键就会卡住,不按就可以正常落下呢?因为摩擦力(真实的物理引擎 )。所以我们要做的就是修改人物碰撞体的材质:
在这里插入图片描述
在这里插入图片描述

  然后来解决第二个问题,只要在代码中进行控制,当人物位于地面上时才允许跳跃就行了:

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

public class PlayerController : MonoBehaviour
{
    void Movement()
    {
        if (Input.GetButtonDown("Jump") && boxCollider2D.IsTouchingLayers(ground))
        {
            rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);
            animator.SetBool("jumping", true);
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

  不过此时人物依然可以蹬墙跳。

12.UI入门

  想让界面显示出任务获得的樱桃个数,那么就需要用到 U I UI UI了:
在这里插入图片描述
  图片和分数我们都想放到屏幕左上角,那么应该通过设置锚点来定位。如果单单使用到中心点的偏移的话,当屏幕大小发生变化时, U I UI UI极有可能出现在错误的位置。
在这里插入图片描述
在这里插入图片描述
  分数的控制由代码实现:

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

public class PlayerController : MonoBehaviour
{
    public Text cherryText;
    
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Collection")
        {
            Destroy(collision.gameObject);
            ++cherry;
            cherryText.text = cherry.ToString();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

13.敌人Enemy!

  首先仿照人物的制作方法制作出一个青蛙敌人,然后将其设为预制体:
在这里插入图片描述
在这里插入图片描述
  青蛙的碰撞体不能当作触发器使用,因为它有 r i g i d b o d y   2 D rigidbody\ 2D rigidbody 2D组件,当成触发器的话会直接掉落。我们希望人物跳起来落到青蛙头上时可以消灭它,同时有一个小跳的效果:

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

public class PlayerController : MonoBehaviour
{
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy" && animator.GetBool("falling"))
        {
            Destroy(collision.gameObject);
            rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);    //小跳效果
            animator.SetBool("jumping", true);
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  不过这里还是存在一些问题的,由于当前的动画设置,人物从高处落下时并不一定处于降落状态,可以考虑把这里的判断条件改为人物的 v e l o c i t y . y < 0 velocity.y<0 velocity.y<0

14.受伤效果Hurt

  首先按照教程把修改代码如下所示:

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

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private bool isHurt = false;

    // Update is called once per frame
    void Update()
    {
        if (!isHurt)//非受伤状态
        {
            Movement();
        }
        SwitchAnimation();
    }

    void SwitchAnimation()
    {
        //animator.SetBool("idle", false);
        if (animator.GetBool("jumping")) //跳跃状态
        {
            if (rb2d.velocity.y < 0)
            {
                animator.SetBool("jumping", false);
                animator.SetBool("falling", true);
            }
        }
        else if (animator.GetBool("falling")) //下落状态
        {
            if (boxCollider2D.IsTouchingLayers(ground))
            {
                animator.SetBool("falling", false);
                animator.SetBool("idle", true);
            }
        }
        else if (isHurt) //受伤状态
        {
            if (Mathf.Abs(rb2d.velocity.x) < 0.1f)//速度过小时认为回到正常状态
            {
                isHurt = false;
            }
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            if (animator.GetBool("falling"))    //掉落状态
            {
                Destroy(collision.gameObject);
                rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);    //小跳效果
                animator.SetBool("jumping", true);
            }
            else if (transform.position.x < collision.gameObject.transform.position.x)//左侧
            {
                isHurt = true;
                rb2d.velocity = new Vector2(-10f, rb2d.velocity.y);
            }
            else if(transform.position.x > collision.gameObject.transform.position.x)//右侧
            {
                isHurt = true;
                rb2d.velocity = new Vector2(10f, rb2d.velocity.y);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

  但是在运行后我发现了一个问题,就是碰撞后人物会一直后退直到碰到其它碰撞体。这是因为教程中使用了两个碰撞体,而我只使用了一个而且把摩擦力改成了 0 0 0,所以我决定通过代码控制受伤状态的速度:

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

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private bool isHurt = false;
    // Update is called once per frame
    void Update()
    {
        if (!isHurt)//非受伤状态
        {
            Movement();
        }
        SwitchAnimation();
    }

    void SwitchAnimation()
    {
        //animator.SetBool("idle", false);
        if (animator.GetBool("jumping")) //跳跃状态
        {
            if (rb2d.velocity.y < 0)
            {
                animator.SetBool("jumping", false);
                animator.SetBool("falling", true);
            }
        }
        else if (animator.GetBool("falling")) //下落状态
        {
            if (boxCollider2D.IsTouchingLayers(ground))
            {
                animator.SetBool("falling", false);
                animator.SetBool("idle", true);
            }
        }
        else if (isHurt) //受伤状态
        {
            int sign = rb2d.velocity.x < 0 ? -1 : 1;
            rb2d.velocity += new Vector2(speed * Time.deltaTime, 0f) * -sign;
            if (Mathf.Abs(rb2d.velocity.x) < 0.1f)
            {
                isHurt = false;
            }
        }
    }


    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            if (animator.GetBool("falling"))    //掉落状态
            {
                Destroy(collision.gameObject);
                rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);    //小跳效果
                animator.SetBool("jumping", true);
            }
            else if (transform.position.x < collision.gameObject.transform.position.x)//左侧
            {
                isHurt = true;
                rb2d.velocity = new Vector2(-5f, rb2d.velocity.y);
            }
            else if(transform.position.x > collision.gameObject.transform.position.x)//右侧
            {
                isHurt = true;
                rb2d.velocity = new Vector2(5f, rb2d.velocity.y);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

  下一步就是制作受伤时的动画了:
在这里插入图片描述

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

public class PlayerController : MonoBehaviour
{
    void SwitchAnimation()
    {
        //animator.SetBool("idle", false);
        if (animator.GetBool("jumping")) //跳跃状态
        {
            if (rb2d.velocity.y < 0)
            {
                animator.SetBool("jumping", false);
                animator.SetBool("falling", true);
            }
        }
        else if (animator.GetBool("falling")) //下落状态
        {
            if (boxCollider2D.IsTouchingLayers(ground))
            {
                animator.SetBool("falling", false);
                animator.SetBool("idle", true);
            }
        }
        else if (isHurt) //受伤状态
        {
            animator.SetBool("hurt", true);
            int sign = rb2d.velocity.x < 0 ? -1 : 1;
            rb2d.velocity += new Vector2(speed * Time.deltaTime, 0f) * -sign;
            if (Mathf.Abs(rb2d.velocity.x) < 0.1f)
            {
                isHurt = false;
                animator.SetBool("hurt", false);
            }
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

15.AI敌人移动

  这里教程采用的方法是在青蛙左右两侧设置两个点,把青蛙的移动区域固定到这两个点内。我这里没有采用这种做法,而是使用了 P h y s i c s 2 D . L i n e c a s t Physics2D.Linecast Physics2D.Linecast判断青蛙前方是否是空地,更具体一点就是判断青蛙脚下是不是地面,以及青蛙头上有没有障碍物:

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

public class Enemy_Frog : MonoBehaviour
{
    private Rigidbody2D rb2d;

    public LayerMask ground;
    public float speed = 5f;
    [SerializeField]
    private bool faceLeft = true;
    // Start is called before the first frame update
    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

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

    void Movement()
    {
        Vector2 frontPosition = transform.position;
        if (faceLeft)
            frontPosition += Vector2.left;
        else
            frontPosition += Vector2.right;
        if (!Physics2D.Linecast(frontPosition + Vector2.down, frontPosition, ground) || Physics2D.Linecast(frontPosition, frontPosition + Vector2.up, ground)) //没有检测到地面 或者头上有障碍物 
        {
            faceLeft = !faceLeft;
            transform.localScale = new Vector3(faceLeft ? 1 : -1, 1, 1); //角色反向
        }
        rb2d.velocity = Vector2.right * (faceLeft ? -speed : speed);
    }

    private void OnDrawGizmos() //debug用
    {
        Vector2 frontPosition = transform.position;
        if (faceLeft)
            frontPosition += Vector2.left;
        else
            frontPosition += Vector2.right;
        Gizmos.DrawLine(frontPosition + Vector2.down, frontPosition + Vector2.up);
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/140736?site
推荐阅读
相关标签
  

闽ICP备14008679号