Unity做一个类似星露谷物语的游戏【1】

前言

星露谷物语这个游戏非常的好玩♂,最近在外包干了两年没有屁的提升,工作也不怎么忙,所以就利用闲暇时间准备做一个类似星露谷物语的游戏,准备把制作过程通过博客的方式记录下来分享给Unity新手和想要学习Unity的好盆友,如果有错误的地方请指正~~
资源下载链接:https://pan.baidu.com/s/11MzqpDdq2qmhiL2wGD3cxg?pwd=z6ik
提取码:z6ik
如果链接被吞了可以私信联系我要资源
工程使用的版本是Unity2022.3.34f1c1
工程是2D

开始要做的事情

由于很多地方需要用到单例模式,每个单例模式都要Instance,代码显得十分的多余,我们可以写一个基类把这些重复的代码作为父类 让子类的代码继承,会节省很多写单例的时间

public abstract class SingleMono<T> : MonoBehaviour where T :MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get { return instance; }
    }
    protected virtual void Awake()
    {
        if (instance == null)
            instance = this as T;
        else
            Destroy(gameObject);
    }
}

上述代码的意思是建立单例instance 在进入Awake的时候就判断instance是否存在,如果不存在就建立这个单例对象如果instance存在的话就说明场景中已经有这个单例,由于单例是只能由一个的所以就要把这个物品删除掉,保证场景中只存在这一个单例

Setting类:所有的固定参数全部放进去,后续会用到的,现在不用理解是什么意思

using UnityEngine;

public static class Settings
{
    //player MoveMent
    public const float runningSpeed = 5.2f;
    public const float walkingSpeed = 2.6f;

    /// player animaion
    public static int isIdle;
    public static int isCarrying;

    public static int inputX;
    public static int inputY;
    public static int isWalking;
    public static int isRunning;
    public static int toolEffect;
    public static int isUsingToolRight;
    public static int isUsingToolLeft;
    public static int isUsingToolUp;
    public static int isUsingToolDown;
    public static int isLiftingToolRight;
    public static int isLiftingToolLeft;
    public static int isLiftingToolUp;
    public static int isLiftingToolDown;
    public static int isPickingRight;
    public static int isPickingLeft;
    public static int isPickingUp;
    public static int isPickingDown;
    public static int isSwingingToolRight;
    public static int isSwingingToolLeft;
    public static int isSwingingToolUp;
    public static int isSwingingToolDown;


    public static int idleUp;
    public static int idleDown;
    public static int idleLeft;
    public static int idleRight;

    static Settings()
    {
        inputX = Animator.StringToHash("xInput");
        inputY = Animator.StringToHash("yInput");
        isWalking = Animator.StringToHash("isWalking");
        isRunning = Animator.StringToHash("isRunning");
        toolEffect = Animator.StringToHash("toolEffect");
        isUsingToolRight = Animator.StringToHash("isUsingToolRight");
        isUsingToolLeft = Animator.StringToHash("isUsingToolLeft");
        isUsingToolUp = Animator.StringToHash("isUsingToolUp");
        isUsingToolDown = Animator.StringToHash("isUsingToolDown");
        isLiftingToolDown = Animator.StringToHash("isLiftingToolDown");
        isLiftingToolLeft = Animator.StringToHash("isLiftingToolLeft");
        isLiftingToolRight = Animator.StringToHash("isLiftingToolRight");
        isLiftingToolUp = Animator.StringToHash("isLiftingToolUp");
        isSwingingToolRight = Animator.StringToHash("isSwingingToolRight");
        isSwingingToolDown = Animator.StringToHash("isSwingingToolDown");
        isSwingingToolLeft = Animator.StringToHash("isSwingingToolLeft");
        isSwingingToolUp = Animator.StringToHash("isSwingingToolUp");
        isPickingDown = Animator.StringToHash("isPickingDown");
        isPickingLeft = Animator.StringToHash("isPickingLeft");
        isPickingRight = Animator.StringToHash("isPickingRight");
        isPickingUp = Animator.StringToHash("isPickingUp");
        idleUp = Animator.StringToHash("idleUp");
        idleDown = Animator.StringToHash("idleDown");
        idleLeft = Animator.StringToHash("idleLeft");
        idleRight = Animator.StringToHash("idleRight");
    }
}

enum类:存放所有的枚举

public enum ToolEffect
{
    none,
    watering,//水葫
}
public enum Direction
{
    up,
    down,
    left,
    right,
    none
}

ToolEffect是粒子特效的枚举
Direction是方向的枚举

创建主角

导入资源,发现有这么多主角的动画状态机,不要怕这些就是对应主角的不同部位,从左到右依次是手臂,身体,头发,帽子,工具和工具粒子
在这里插入图片描述
在场景中创建一个EmptyGameObject 起名叫Player 然后让它作为父节点创建6个子物体,添加SpriteRenderer组件和Animator组件,把这些状态机拖拽到对应的Animator上
Player上添加RigidBody2D用于后续的碰撞检测,添加SorttingGroup组件来防止子节点的图片图层混乱
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样我们的主角就创建好了~

主角的动画

我们随便打开一个动画状态机发现,妈蛋!参数怎么这么多这可怎么搞啊!! 不要怕且听我慢慢道来~
在这里插入图片描述
首先简单介绍一下这些参数的意思:
XInput,YInput是向左向右的输入
IsWalking 是否为移动状态
IsRuning 是否为奔跑状态
toolEffect是粒子特效的种类 比如浇水时候的水,割草时候的草
isxxxToolxxx是拿着某个工具面向某个方向进行的动作,稍后写个动画测试工具就可以知道相应的参数对应的意思了
下面知道了参数的意思了我们就可以写对应的代码了

我们来用观察者模式来写动画的触发
这里就默认大家会观察者模式了哈

public delegate void MovementDelegate(float inputX,float inputY,bool isWalking,bool isRunning,bool isIdle,bool isCarrying
    ,ToolEffect toolEffect,
    bool isUseToolRight,bool isUseToolLeft,bool isUsingToolUp,bool isUsingToolDown,
    bool isLiftToolRight,bool isLiftToolLeft,bool isLiftToolUp,bool isLiftToolDown,
    bool isPickRight, bool isPickLeft, bool isPickUp, bool isPickDown,
    bool isSwingingToolRight, bool isSwingingToolLeft, bool isSwingingToolUp, bool isSwingingToolDown,
    bool idleUp,bool idleDown ,bool idleLeft,bool idleRight);
public static class EventHandler
{
    //moveEvent
    public static event MovementDelegate MovementEvent;
    //movement广播
    public static void CallMoveMentEvent(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying
    , ToolEffect toolEffect,
    bool isUseToolRight, bool isUseToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftToolRight, bool isLiftToolLeft, bool isLiftToolUp, bool isLiftToolDown,
    bool isPickRight, bool isPickLeft, bool isPickUp, bool isPickDown,
    bool isSwingingToolRight, bool isSwingingToolLeft, bool isSwingingToolUp, bool isSwingingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight)
    {
        if (MovementEvent != null)
        {
            MovementEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying,
                toolEffect,
                isUseToolRight, isUseToolLeft, isUsingToolUp, isUsingToolDown,
                isLiftToolRight, isLiftToolLeft, isLiftToolUp, isLiftToolDown,
                isPickRight, isPickLeft, isPickUp, isPickDown,
                isSwingingToolRight, isSwingingToolLeft, isSwingingToolUp, isSwingingToolDown,
                idleUp, idleDown, idleLeft, idleRight);
        }
    }
}

这里的参数就是对应动画状态机里面的参数
然后我们来写相应的动画代码

public class Move : MonoBehaviour
{
    private Animator animator;
    private void Start()
    {
        animator = gameObject.GetComponent<Animator>(); 
    }
    private void OnEnable()
    {
        EventHandler.MovementEvent += SetAnimationParameters;//进入的时候订阅MovementEvent
    }
    private void OnDisable()
    {
        EventHandler.MovementEvent -= SetAnimationParameters;//删除的时候取消订阅
    }
    private void SetAnimationParameters(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying
    , ToolEffect toolEffect,
    bool isUseToolRight, bool isUseToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftToolRight, bool isLiftToolLeft, bool isLiftToolUp, bool isLiftToolDown,
    bool isPickRight, bool isPickLeft, bool isPickUp, bool isPickDown,
    bool isSwingingToolRight, bool isSwingingToolLeft, bool isSwingingToolUp, bool isSwingingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight)
    {
        animator.SetFloat(Settings.inputX, inputX);
        animator.SetFloat(Settings.inputY, inputY);
        animator.SetBool(Settings.isWalking, isWalking);
        animator.SetBool(Settings.isRunning, isRunning);
        animator.SetBool(Settings.isUsingToolDown, isUsingToolDown);
        animator.SetBool(Settings.isUsingToolLeft, isUseToolLeft);
        animator.SetBool(Settings.isUsingToolRight, isUseToolRight);
        animator.SetBool(Settings.isUsingToolUp, isUsingToolUp);

        animator.SetBool(Settings.isLiftingToolRight, isLiftToolRight);
        animator.SetBool(Settings.isLiftingToolLeft, isLiftToolLeft);
        animator.SetBool(Settings.isLiftingToolUp, isLiftToolUp);
        animator.SetBool(Settings.isLiftingToolDown, isLiftToolDown);

        animator.SetBool(Settings.isPickingRight, isPickRight);
        animator.SetBool(Settings.isPickingLeft, isPickLeft);
        animator.SetBool(Settings.isPickingUp, isPickUp);
        animator.SetBool(Settings.isPickingDown, isPickDown);

        animator.SetBool(Settings.isSwingingToolRight, isSwingingToolRight);
        animator.SetBool(Settings.isSwingingToolLeft, isSwingingToolLeft);
        animator.SetBool(Settings.isSwingingToolUp, isSwingingToolUp);
        animator.SetBool(Settings.isSwingingToolDown, isSwingingToolDown);

        animator.SetBool(Settings.idleUp, idleUp);
        animator.SetBool(Settings.idleDown, idleDown);
        animator.SetBool(Settings.idleLeft, idleLeft);
        animator.SetBool(Settings.idleRight, idleRight);

        animator.SetInteger(Settings.toolEffect,(int)toolEffect);

    }

将Move.cs添加到Player的各个子物体上,就可以实现角色的动画了,是不是特别的神奇,这就是观察者模式的牛逼之处,降低了代码的耦合性

来测试一下动画是否有问题

我们可以创建一个类,把动画的参数全部暴露在Inspector面板中,来调试观察动画是不是真正的改变了

public class PlayerAnimatorTest : MonoBehaviour
{
    public  bool isIdle;
    public  bool isCarrying; 
    public  float inputX;
    public  float inputY;
    public  bool isWalking;
    public  bool isRunning;
    public  ToolEffect toolEffect;
    public  bool isUsingToolRight;
    public  bool isUsingToolLeft;
    public  bool isUsingToolUp;
    public  bool isUsingToolDown;
    public  bool isLiftingToolRight;
    public  bool isLiftingToolLeft;
    public  bool isLiftingToolUp;
    public  bool isLiftingToolDown;
    public  bool isPickingRight;
    public  bool isPickingLeft;
    public  bool isPickingUp;
    public  bool isPickingDown;
    public  bool isSwingingToolRight;
    public  bool isSwingingToolLeft;
    public  bool isSwingingToolUp; 
    public  bool isSwingingToolDown; 
    public  bool idleUp;
    public  bool idleDown;
    public  bool idleLeft;
    public  bool idleRight;
    private void Update()
    {
        EventHandler.CallMoveMentEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying
    , toolEffect,
      isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
      isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
      isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
      isSwingingToolRight, isSwingingToolLeft, isSwingingToolUp, isSwingingToolDown,
      idleUp, idleDown, idleLeft, idleRight); 
    }
}

这个代码可以挂在任何一个物体上,没有什么影响,运行游戏后点击对应的值就会播放对应的动画啦,这样也可以理解状态机中各个参数的意思,如果动画没有播放的话就要去Move函数中动画播放的逻辑中观察参数是否和动画状态机的参数一样

角色的移动

现在终于来到最重要的地方了,就是让自己做的这个角色动起来,是不是很激动啊!
首先玩家只有一个所以它是一个单例,要继承SingleMono类
类成员变量也是对应的动画状态机的各个参数
下面的代码感觉大家都可以明白我就不啰嗦解释了,如果有不明白的可以留言或者私信问我哦
做好之后把代码挂到Player上就可以实现简单的移动和对应的动画播放啦

public class Player : SingleMono<Player>
{
    //MoveMent
    private bool isIdle;
    private bool isCarrying = false;

    private float inputX;
    private float inputY;
    private bool isWalking;
    private bool isRunning;
    private ToolEffect toolEffect;
    private bool isUsingToolRight;
    private bool isUsingToolLeft;
    private bool isUsingToolUp;
    private bool isUsingToolDown;
    private bool isLiftingToolRight;
    private bool isLiftingToolLeft;
    private bool isLiftingToolUp;
    private bool isLiftingToolDown;
    private bool isPickingRight;
    private bool isPickingLeft;
    private bool isPickingUp;
    private bool isPickingDown;
    private bool isSwingingToolRight;
    private bool isSwingingToolLeft;
    private bool isSwingingToolUp;
    private bool isSwingingToolDown;
    private Rigidbody2D rigid2d;
    private Direction playerDirection;
    private float movementSpeed; 
    protected override void Awake()
    {
        base.Awake();
        rigid2d = GetComponent<Rigidbody2D>();
    }
    private void Update()
    {
        #region PlayerIpnut
        ResetAnimatorTrigger();
        PlayerInput();
        PlayerWalkInput();
        EventHandler.CallMoveMentEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying
    , toolEffect,
      isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
      isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
      isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
      isSwingingToolRight, isSwingingToolLeft, isSwingingToolUp, isSwingingToolDown,
      false, false, false, false);
        #endregion
    }
    private void FixedUpdate()
    {
        PlayerMoveMent();
    }
    /// <summary>
    /// 所有动画全部重置
    /// </summary>
    private void ResetAnimatorTrigger()
    {
        isUsingToolRight = false;
        isUsingToolLeft = false;
        isUsingToolUp = false;
        isUsingToolDown = false;
        isLiftingToolRight = false;
        isLiftingToolLeft = false;
        isLiftingToolUp = false;
        isLiftingToolDown = false;
        isPickingRight = false;
        isPickingLeft = false;
        isPickingUp = false;
        isPickingDown = false;
        isSwingingToolRight = false;
        isSwingingToolLeft = false;
        isSwingingToolUp = false;
        isSwingingToolDown = false;
        toolEffect = ToolEffect.none;
    }
    /// <summary>
    /// 玩家输入
    /// </summary>
    private void PlayerInput()
    {
        inputY = Input.GetAxisRaw("Vertical");
        inputX = Input.GetAxisRaw("Horizontal");
        //斜着走慢一点
        if (inputX != 0 &&inputY != 0)
        {
            inputX *= 0.71f;
            inputY *= 0.71f;
        }
        if (inputX != 0 || inputY != 0)
        {
            isWalking = true;
            isRunning = false;
            isIdle = false;
            movementSpeed = Settings.walkingSpeed;
            if (inputX < 0) playerDirection = Direction.left;
            if (inputX > 0) playerDirection = Direction.right;
            if (inputY < 0) playerDirection = Direction.down;
            if (inputY > 0) playerDirection = Direction.up;
        }
        else if (inputX == 0 && inputY == 0)
        {
            isRunning = false;
            isWalking = false;
            isIdle = true;
        }
    }
    /// <summary>
    /// 是否奔跑
    /// </summary>
    private void PlayerWalkInput()
    {
        if (Input.GetKey(KeyCode.LeftShift)|| Input.GetKey(KeyCode.RightShift))
        {
            isWalking = false;
            isRunning = true;
            isIdle = false;
            movementSpeed = Settings.runningSpeed;
            Debug.Log("isRun");
        }
        else
        {
            isWalking = true;
            isRunning = false;
            isIdle = false;
            movementSpeed = Settings.walkingSpeed;
            Debug.Log("RunOver");

        }
    }
    private void PlayerMoveMent()
    {
        Vector2 move = new Vector2(inputX * movementSpeed * Time.deltaTime, inputY * movementSpeed * Time.deltaTime);
        rigid2d.MovePosition(rigid2d.position + move);
    }
}

摄像机跟随移动

给大家介绍一个很牛逼的摄像机插件
首先我们要打开Windows->Package Manager,进入页面后搜索Chinemachine下载此插件,你就可以拥有一个很牛逼的摄像机插件了!
该插件的使用方法可以去这个大佬的博客里看一下 写的很详细:https://blog.csdn.net/linxinfa/article/details/124537415
在这里插入图片描述
项目的使用:我们要在MainCamera下添加CinemachineBrain组件
然后创建一个空物体给它添加组件CinemachineVirtualCamera 然后在Follow栏把Player拖进去就行了
这样摄像机就会像狗皮膏药一样跟着你这个主角到处走啦
在这里插入图片描述

在这里插入图片描述

结语

好啦!时间不早,代码刚好,第一天的项目只是开胃小菜,用到的知识点还是挺多的,如果您喜欢我的这篇文章请留言淼哥好帅!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值