RGP2D小游戏第一天

2,435 阅读10分钟

一、前期准备

image.png 1.Grid组件

要想制作上图的地图效果,主要是通过Grid组件来实现,通过Grid组件有助于根据选定的布局来对齐游戏对象的导引网格。该组件将网格单元格位置转换为游戏对象的相应__局部坐标 。然后,[Transform]组件将这些局部坐标转换为世界空间或__全局坐标**。

image.png

参数列表:

image.png

二、新建空对象Grid,里面包含以下元素:

image.png

同时将Grid挂载Grid组件,最外层理解是一个大网格,绑定了Grid组件才能绘制小地图。

image.png

同时Grid里面的11个空对象分别挂载Tilemap、Timemap Render组件

2.Tilemap组件

Tilemap 一般称之为瓦片地图或者平铺地图,是 Unity2017 中新增的功能,主要用于快速编辑 2D 游戏中的场景,通过复用资源的形式提升地图多样性

​ 工作原理就是用一张张的小图为一张大地图

3.Tilemap Render组件

Tilemap Renderer 组件用于渲染场景中的[Tilemap],Unity 会创建默认附加了 Tilemap Renderer 组件的瓦片地图

渲染模式 (Render Mode) 会影响瓦片地图精灵渲染时的排序方式。因此我们要设置一定的排序图层。可以理解为Photoshop里面的图层顺序,有的物体是在上面显示的,有的图层是要在最下面的。所有针对于11个空对象分别设置它们的图层顺序

image.png

根据步骤弄完上面好后,我们就可以开始绘制地图了。

给Grid挂载Grid组件后,我们会发现在面板右上角会出现网格的图标。

image.png

点击图标,打开平铺调色板。

新建调色板, image.png

image.png 单独创建Pattle调色板文件夹,里面存放的是所有创建的调色板信息

image.png

新建后,将地图拖拽到调色板中

image.png

这时候我们把对应的画板信息存储在Brush文件夹管理。

image.png

最后,我们根据选择不同的调色板和不同的图层画地图了。(比如说你石头是放在石头图层,树是放在树的图层即可)

image.png

二、前期准备后续

当然了后续也是直接导入地图资源,我们只需要关注怎么代码操作了。

导入以下资源包:

image.png

百度网盘链接: 链接:pan.baidu.com/s/1g7OuhEty… 提取码:pbzw

我们直接创建一个2021版的unity2D项目,然后创建对应的图层以及修改好对应的图层显示即可。

三、实现2D人物移动

给Player物体、以及所有的树添加Sorting Group组件,让他们参与Grid的11个空对象融入排序,并且 给Player物体添加刚体、盒状碰撞器,

image.png

技巧:碰撞在游戏中是经常的事,我们可以理解为把动态的要移动的物体添加刚体以及碰撞器,把静态的添加碰撞器,并且需要勾选为是触发器。这样我们就能触发对应的碰撞方法,能进行碰撞检测。

image.png

在这里我们是给人物的脚区域设置为盒状碰撞器区域的,因为只有脚走到了才能碰撞,不能整个身体都是碰撞区域。

image.png

这时候我们实现人物的移动操作:

编写Player脚本文件:

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

public class Player : MonoBehaviour
{
    private Rigidbody2D rig;
    private float inputX;
    private float inputY;
    private Vector2 movementValue;
    public float speed;
    private void Awake()
    {
        rig = GetComponent<Rigidbody2D>();//获取刚体组件
    }
    void Update()
    {
        getMoveValue();
    }
    private void FixedUpdate()
    {
        move();
    }
    //获取x,y的偏移量变化值  
    private void getMoveValue()
    {
        inputX = Input.GetAxisRaw("Horizontal");
        inputY = Input.GetAxisRaw("Vertical");
        movementValue = new Vector2(inputX,inputY);
        //print(movementValue.normalized);//单位向量化    
        movementValue = movementValue.normalized; 
        //这里使用normalized作用就是可以将movementValue的值保持水平和垂直的移动距离是一样的,
        //如果不加这个的话,你同时按下右下的距离是根号2,距离比水平和垂直的移动距离是不相等的。
    }
    //实现人物的移动
    private void move()
    {
        rig.MovePosition(rig.position+movementValue*Time.deltaTime*speed);
    }
}

如果你需要参考normalized的作用,可以参考文章:juejin.cn/post/720784…

编写完后就可以实现人物的移动l。

同时我们把树整体设置两个盒状碰撞器,分别将树叶化为一个区域,将树干化为一个区域,同时树的trunk的地步单独设置一个盒状碰撞器。如下:

image.png

这里的green的添加了Sprite Render组件,这个功能是设置精灵图,将树叶拖入精灵图源中即可。其他的树也是一样的操作,这里我们添加完一棵树的碰撞器后,可以直接使用复制组件来复用下面的树

image.png

四、人物经过树实现淡入淡出

新建settings脚本文件,新建后我们发现不是一个脚本的图标,而是一个齿轮的图标,这里面存放是我们经常要使用到的常量:是不需要挂载到其他地方的

public class settings
{
    public const float fadeColorAlpha = 0.3f; //颜色淡入淡出透明度最低值
    public const float fadeColorDuration = 0.7f; //淡入淡出的持续时间
}

要实现这种树叶淡入淡出即获取树叶的透明度,当人物碰撞的时候淡入,人物走开就淡出。即我们创建ItemFader脚本,这个脚本功能是实现淡入淡出的方法

ItemFader:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening; //引入命名空间
public class ItemFader : MonoBehaviour
{
    SpriteRenderer spriteRander;
    private void Awake()
    {
        //获取对应的SpriteRendered组件
        spriteRander = GetComponent<SpriteRenderer>();
    }
    //淡出fadeOut
    public void fadeOut()
    {
        Color colorTarget = new Color(1,1,1,settings.fadeColorAlpha); //settings.fadeColorAlpha淡入淡出颜色最低值
        spriteRander.DOColor(colorTarget,settings.fadeColorDuration); //必须勾选工具选项卡中的Sprite

    }
    
    //淡入fadeIn
    public void fadeIn()
    {
        Color colorTarget = new Color(1, 1, 1,1);
        spriteRander.DOColor(colorTarget, settings.fadeColorDuration);
    }
}

这里用到了一个组件DGTween插件,打开包管理器搜索下载即可。

image.png 通过这个插件我们可以设置树叶的透明度。前提是必须获取到SpriteRender组件,通过这个组件可以调用DOColor方法,参数一个是对应的颜色,另一个参数对应的是颜色的持续时间。

 Color colorTarget = new Color(1,1,1,settings.fadeColorAlpha); //settings.fadeColorAlpha淡入淡出颜色最低值
        spriteRander.DOColor(colorTarget,settings.fadeColorDuration); //必须勾选工具选项卡中的Sprite

编写ItemFaderTrigger,挂载到Player身上,实现碰撞检测:

public class ItemFaderTrigger : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D other)
    {
        print("触发了");
        //给所有碰到的触发器的孩子对应的ItemFader脚本,然后调用fadeOut方法,更改它的颜色透明度
        ItemFader[] itemList = other.GetComponentsInChildren<ItemFader>();
        foreach (var item in itemList)
        {
            item.fadeOut();//靠近淡出树叶
        }
    }
    private void OnTriggerExit2D(Collider2D other)
    {
        ItemFader[] itemList = other.GetComponentsInChildren<ItemFader>();
        foreach (var item in itemList)
        {
            item.fadeIn();//离开则淡入树叶,恢复原来树的颜色,透明度为1
        }
    }
}

这样就大功告成了,实现了淡入淡出效果。

五、效果如下:

移动.gif

六、人物基本动画和行走

根据上期制作的虽然可以进行人物的走动,但是人物走动并没有加入动画,这期我们加入动画。 首先在Animation文件夹新建Player,里面存放的是Body,Hand,Hair三个对应的身体、手、和头发的动画。

点击Body,再打开动画选项卡,选择新建动画,存放好对应文件夹后,创建一个动画后会默认创建一个动画控制器,并且会自动绑定在对应的物体上。

双击创建好的body动画控制器,在里面新建一个混合树

image.png

双击混合树,创建两个float变量,x、y控制人物的上下左右斜着走的速度或者是状态,修改混合树的混合类型为:2D Freeform Directional

image.png 并且将创建好的x、y变量关联到混合树中:

image.png

这时候我们就要开始制作动画了,新建好动画命名为body的walkDown,在Assets\M Studio\Art\Characters\Player\目录下找到对应的walkdown图片,将全部照片8张拖入到动画格中,帧数值改为20。(Samples:帧数,设置动画在一秒钟之内播放的帧数,数值越大,动画的播放速度越快,反之越小)

image.png 在新建一个walkIdle,这个动画是控制不动状态下的动画,只需要拖入一个图片即可:

image.png 以此重复上面的操作,创建好walkUo walkUpIdle;walkLeft walkleftIdle,walkRight walkRightIdle。创建好后,我们可以进行混合树的编辑了。 将创建好的动画拖入混合树运动域中,设置好值,如下:

image.png 分别用y为1的值来控制上,用0.1来控制向上的不走动的状态。相反往下就是-1,-0.1,左右就是x为-1或1来控制行走,同时当我们按下shift键,会进行加速度的状态,这个也需要动画和速度的设置,加速度使用2的值来设置。

其他的头部、手部也是统一跟身体一样设置好。

image.png

image.png 接下来就是代码编辑部分了,在Player脚本的基础上添加功能。

关联三个控制器:

//关联身体,手,头发的控制器
    public Animator body;
    public Animator hand;
    public Animator hair;

image.png Player.cs脚本

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

public class Player : MonoBehaviour
{
    private Rigidbody2D rig;
    private float inputX;
    private float inputY;
    private Vector2 movementValue; //人物需要移动的速度
    public float speed;//移动速度的倍率
    //关联身体,手,头发的控制器
    public Animator body;
    public Animator hand;
    public Animator hair;

    //记录上一帧的值
    private float inputXX;
    private float inputYY;
    private void Awake()
    {
        rig = GetComponent<Rigidbody2D>();//获取刚体组件
    }
    void Update()
    {
        getMoveValue();
        animateMove();//动画变化的方法
    }

    private void animateMove()
    {
        //inputX,inputY 这两个偏移量在 -1,0,1三个值之间进行变化 保证停下来对应状态的值不能是0
        print($"inputX:{inputX}");
        print($"inputY:{inputY}");
        if(inputX==0&&inputY==0)
        {
            //如果一开始没按下键盘或者键盘全部松开的时候才要切换到默认的状态,也就是动滑里面的idle状态

            //如果上一帧按下了键盘,但是又停下来了,这时候我们要设置的是上一帧的idle的值,这里就可以在设置另外两个变量来监听上一帧的水平和垂直的值咯
            body.SetFloat("x", inputXX*0.1f);
            body.SetFloat("y", inputYY*0.1f);
            hair.SetFloat("x", inputXX * 0.1f);
            hair.SetFloat("y", inputYY * 0.1f);
            hand.SetFloat("x", inputXX * 0.1f);
            hand.SetFloat("y", inputYY * 0.1f);
        }
        else
        {
            //如果inputX,inputY不为0,说明按下了,获取水平和垂直虚拟轴
            inputXX = Input.GetAxisRaw("Horizontal");//记录一下上一次inputX的值
            inputYY = Input.GetAxisRaw("Vertical");
            //如果按下shift,速度*2
            if (Input.GetKey(KeyCode.LeftShift))
            {
                //GetKeyDown只会检测一次,在这里我们要不断地进行检测,所以要使用GetKey
                body.SetFloat("x", inputX*2);
                body.SetFloat("y", inputY * 2);
                hair.SetFloat("x", inputX * 2);
                hair.SetFloat("y", inputY * 2);
                hand.SetFloat("x", inputX * 2);
                hand.SetFloat("y", inputY * 2);
            }
            else
            {
                //没有按下shift,直接将获取的水平和垂直虚拟轴值赋值给控制动画的变量,
                body.SetFloat("x", inputX);
                body.SetFloat("y", inputY);
                hair.SetFloat("x", inputX);
                hair.SetFloat("y", inputY);
                hand.SetFloat("x", inputX);
                hand.SetFloat("y", inputY);
            }
        }
        
    }

    private void FixedUpdate()
    {
        move();
    }
    //获取x,y的偏移量变化值  
    private void getMoveValue()
    {
        inputX = Input.GetAxisRaw("Horizontal");
        inputY = Input.GetAxisRaw("Vertical");
        movementValue = new Vector2(inputX,inputY);
        //print(movementValue.normalized);//单位向量化    
        movementValue = movementValue.normalized; 
        //这里使用normalized作用就是可以将movementValue的值保持水平和垂直的移动距离是一样的,
        //如果不加这个的话,你同时按下右下的距离是根号2,距离比水平和垂直的移动距离是不相等的。
    }
    //实现人物的移动
    private void move()
    {
        rig.MovePosition(rig.position+movementValue*Time.deltaTime*speed);
    }
}


七、Cinemachine在2D横版游戏中的使用方法简介

Cinemachine的下载及导入

现在的版本不再需要从assets store中下载了,直接从[package](https://so.csdn.net/so/search?q=package&spm=1001.2101.3001.7020) manager中就能找到Cinemachine这个插件。初次找到,请点击Download下载!

image.png

2.创建Cinemachine对象

并将当前人物拉到Follow以及LookAt中。

image.png

现在就可以实现相机自动人物跟随了。2D不像3D那样,3D实现人物相机跟随需要编写代码计算人物和相机的距离以及面向角度。

3.参数介绍

  1. Follow : 跟随那个目标
  2. Look at : 看向哪个目标(3D游戏中经常使用,由于是2D游戏,不需要控制旋转,所以默认None就行了)
  3. Vertical VOF(Lens中):该值越大则摄像机距离2D平面越远,game视图中看到的地图,玩家等越小。该值越小则摄像机越近,但太近会看到非常模糊的一个个像素。