赞
踩
首先我之前在b站的时候突然发现有个大佬说复刻了空洞骑士,点进去一看发现很多场景都福源道非常详细,当时我除了觉得大佬很强的同时也想自己试一下,而且当时对玩家血条设计等都很模糊,就想着问up主,结果因为制作的时间过了很久了,大佬也有些答不上来,于是我就先下来,然后一直跟着其它视频继续学,这几天闲着就试着通过大佬的代码能不能逐步做一个空洞骑士的mod出来,所幸前面的步骤都比较顺利,通过大佬的代码还是能慢慢做出来
(Steam截图镇个楼)
大佬的视频以及Github源码:
【Unity3D】空洞骑士の复刻_哔哩哔哩_bilibili
项目开源:https://github.com/dreamCirno/Hollow-Knight-Imitation
打开开源项目,先别一次性把Assets的项目全部导入,不然肯定一堆报错的,我们先把角色的精灵图导入,然后再拖入几个地板,然后场景就暂时这样了。
接着我们要为玩家创建动作了。
创建Input Actions命名为InputControl,然后这些都是老操作了。
然后就生成一个C#脚本名字就叫InputControl,然后创建一个名字叫InputManger的空对象以及一个同名脚本、
我们暂时只用到GamePlayer的动作表所以就先这样写了。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class InputManager : MonoBehaviour
- {
- private static InputControl inputControl;
- public static InputControl InputControl
- {
- get
- {
- if(inputControl == null)
- {
- inputControl = new InputControl();
- }
- return inputControl;
- }
- }
-
- private void OnEnable()
- {
- InputControl.GamePlayer.Movement.Enable();
- InputControl.GamePlayer.Jump.Enable();
- InputControl.GamePlayer.Attack.Enable();
- }
-
- private void OnDisable()
- {
- InputControl.GamePlayer.Movement.Disable();
- InputControl.GamePlayer.Jump.Disable();
- InputControl.GamePlayer.Attack.Disable();
- }
-
-
-
- }
我们为我们的Player创建一个名字叫CharacterController2D的脚本。
然后为我们的Player对象添加上组件
2D物理材质如下
首先我们先实现玩家的移动和转向
对于移动我们采用InputSystem对于行为动作的订阅事件和退订事件,用vectorInput读入键盘的输入,
对于转向则根据任务面部朝向,当向右移动的时候transform.localScale为(-1,1,1),向左则为(1,1,1);
- using Com.LuisPedroFonseca.ProCamera2D;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.InputSystem;
-
- public class CharacterController2D : MonoBehaviour
- {
- #region Propertries
- readonly Vector3 flippedScale = new Vector3(-1, 1, 1);
-
- private Rigidbody2D controllerRigibody;
-
- [Header("依赖脚本")] Animator animator;
-
- [Header("移动参数")]
- [SerializeField] float maxSpeed = 0.0f;
- [SerializeField] float maxGravityVelocity = 10.0f;
- [SerializeField] float jumpForce = 0.0f;
- [SerializeField] float groundedGravityScale = 0.0f;
-
- [SerializeField] float jumpGravityScale = 0.0f;
- [SerializeField] float fallGravityScale = 0.0f;
-
- private Vector2 vectorInput;
- private int jumpCount;
- private bool JumpInput;
- private float counter;
-
- private bool enableGravity;
- private bool canMove;
-
- private bool isOnGround;
- private bool isFacingLeft;
- private bool isJumping;
- private bool isFalling;
-
- private int animatorFirstLandingBool;
- private int animatorGroundedBool;
- private int animatorMovementSpeed;
- private int animatorVelocitySpeed;
- private int animatorJumpTrigger;
- private int animatorDoubleJumpTrigger;
-
- [Header("其它参数")]
- [SerializeField] private bool firstLanding;
-
- #endregion
-
- #region CallBackFunctions
- private void Awake()
- {
- controllerRigibody = GetComponent<Rigidbody2D>();
- animator = GetComponent<Animator>();
- }
-
- private void OnEnable()
- {
- InputManager.InputControl.GamePlayer.Movement.performed += ctx => vectorInput = ctx.ReadValue<Vector2>();
- InputManager.InputControl.GamePlayer.Jump.started += Jump_Started;
- InputManager.InputControl.GamePlayer.Jump.performed += Jump_Performed;
- InputManager.InputControl.GamePlayer.Jump.canceled += Jump_Canceled;
- }
-
- private void OnDisable()
- {
- InputManager.InputControl.GamePlayer.Movement.performed -= ctx => vectorInput = ctx.ReadValue<Vector2>();
- InputManager.InputControl.GamePlayer.Jump.started -= Jump_Started;
- InputManager.InputControl.GamePlayer.Jump.performed -= Jump_Performed;
- InputManager.InputControl.GamePlayer.Jump.canceled -= Jump_Canceled;
- }
-
- private void Start()
- {
- animatorFirstLandingBool = Animator.StringToHash("FirstLanding");
- animatorGroundedBool = Animator.StringToHash("Grounded");
- animatorVelocitySpeed = Animator.StringToHash("Velocity");
- animatorMovementSpeed = Animator.StringToHash("Movement");
- animatorJumpTrigger = Animator.StringToHash("Jump");
- animatorDoubleJumpTrigger = Animator.StringToHash("DoubleJump");
-
- animator.SetBool(animatorFirstLandingBool, firstLanding);
-
- enableGravity = true;
- canMove = true;
- }
-
- private void FixedUpdate()
- {
- UpdateVelocity();
- UpdateDirection();
-
- }
-
- #endregion
-
- #region Movement
-
- private void UpdateVelocity()
- {
- Vector2 velocity = controllerRigibody.velocity;
- if (vectorInput.x != 0)
- {
- velocity.y = Mathf.Clamp(velocity.y, -maxGravityVelocity / 2, maxGravityVelocity / 2);
- }
- else
- {
- velocity.y = Mathf.Clamp(velocity.y, -maxGravityVelocity, maxGravityVelocity);
- }
- animator.SetFloat(animatorVelocitySpeed, controllerRigibody.velocity.y);
- if (canMove)
- {
- controllerRigibody.velocity = new Vector2(vectorInput.x * maxSpeed, velocity.y);
- animator.SetInteger(animatorMovementSpeed, (int)vectorInput.x);
- }
- }
-
- private void UpdateDirection()
- {
- //控制玩家的旋转
- if (controllerRigibody.velocity.x > 1f && isFacingLeft)
- {
- isFacingLeft = false;
- transform.localScale = flippedScale;
- }
- else if (controllerRigibody.velocity.x < -1f && !isFacingLeft)
- {
- isFacingLeft = true;
- transform.localScale = Vector3.one;
- }
- }
-
-
-
-
- private void UpdateGrounding(Collision2D collision,bool exitState)
- {
- if (exitState)
- {
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian") || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian"))
- {
- isOnGround = false;
-
- }
- }
- else
- {
- //判断为落地状态
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian")
- || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian")
- && collision.contacts[0].normal == Vector2.up
- && !isOnGround)
- {
- isOnGround = true;
- isJumping = false;
- isFalling = false;
- }
- //判断为头顶碰到物体状态
- else if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian") || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian")
- && collision.contacts[0].normal == Vector2.down && isJumping)
- {
-
- }
- }
- animator.SetBool(animatorGroundedBool, isOnGround);
- }
-
- public void StopHorizontalMovement()
- {
- Vector2 velocity = controllerRigibody.velocity;
- velocity.x = 0;
- controllerRigibody.velocity = velocity;
- animator.SetInteger(animatorMovementSpeed, 0);
- }
-
- public void SetIsOnGrounded(bool state)
- {
- isOnGround = state;
- animator.SetBool(animatorGroundedBool, isOnGround);
- }
- #endregion
-
- #region Combat
- private void Jump_Canceled(InputAction.CallbackContext context)
- {
-
- }
-
- private void Jump_Performed(InputAction.CallbackContext context)
- {
-
- }
-
- private void Jump_Started(InputAction.CallbackContext context)
- {
-
- }
-
- private void OnCollisionEnter2D(Collision2D collision)
- {
- UpdateGrounding(collision, false);
- }
-
- private void OnCollisionStay2D(Collision2D collision)
- {
- UpdateGrounding(collision, false);
- }
-
- private void OnCollisionExit2D(Collision2D collision)
- {
- UpdateGrounding(collision, true);
- }
- #endregion
-
- #region Others
-
- public void FirstLanding()
- {
-
- }
-
- #endregion
-
- }
接着我们制作动画,制作好Idle,walk,Run的动画
由于我们还没为动画判断条件Grounded作代码判断条件,所以就先创建一个空对象用于地面检测
再给他一个脚本
- using UnityEngine;
-
- public class GroundDetector : MonoBehaviour
- {
- private CharacterController2D character;
-
- private void Awake()
- {
- character = FindObjectOfType<CharacterController2D>();
- }
-
- private void OnTriggerEnter2D(Collider2D collision)
- {
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian"))
- {
- character.SetIsOnGrounded(true);
- }
- }
-
- private void OnTriggerExit2D(Collider2D collision)
- {
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian"))
- {
- character.SetIsOnGrounded(false);
- }
- }
- }
移动的脚本做完了我们还需要做跳跃,跳跃分为一段跳和二段跳,首先打开CharacterController2D,我们将通过跳跃计数器决定播放一段跳或是二段跳的动画,并通过判断条件决定什么时候重置动画
- using Com.LuisPedroFonseca.ProCamera2D;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.InputSystem;
-
- public class CharacterController2D : MonoBehaviour
- {
- #region Propertries
- readonly Vector3 flippedScale = new Vector3(-1, 1, 1);
-
- private Rigidbody2D controllerRigibody;
-
- [Header("依赖脚本")] Animator animator;
-
- [Header("移动参数")]
- [SerializeField] float maxSpeed = 0.0f;
- [SerializeField] float maxGravityVelocity = 10.0f;
- [SerializeField] float jumpForce = 0.0f;
- [SerializeField] float groundedGravityScale = 0.0f;
-
- [SerializeField] float jumpGravityScale = 0.0f;
- [SerializeField] float fallGravityScale = 0.0f;
-
- private Vector2 vectorInput;
- private int jumpCount;
- private bool JumpInput;
- private float counter;
-
- private bool enableGravity;
- private bool canMove;
-
- private bool isOnGround;
- private bool isFacingLeft;
- private bool isJumping;
- private bool isFalling;
-
- private int animatorFirstLandingBool;
- private int animatorGroundedBool;
- private int animatorMovementSpeed;
- private int animatorVelocitySpeed;
- private int animatorJumpTrigger;
- private int animatorDoubleJumpTrigger;
-
- [Header("其它参数")]
- [SerializeField] private bool firstLanding;
-
- #endregion
-
- #region CallBackFunctions
- private void Awake()
- {
- controllerRigibody = GetComponent<Rigidbody2D>();
- animator = GetComponent<Animator>();
- }
-
- private void OnEnable()
- {
- InputManager.InputControl.GamePlayer.Movement.performed += ctx => vectorInput = ctx.ReadValue<Vector2>();
- InputManager.InputControl.GamePlayer.Jump.started += Jump_Started;
- InputManager.InputControl.GamePlayer.Jump.performed += Jump_Performed;
- InputManager.InputControl.GamePlayer.Jump.canceled += Jump_Canceled;
- }
-
- private void OnDisable()
- {
- InputManager.InputControl.GamePlayer.Movement.performed -= ctx => vectorInput = ctx.ReadValue<Vector2>();
- InputManager.InputControl.GamePlayer.Jump.started -= Jump_Started;
- InputManager.InputControl.GamePlayer.Jump.performed -= Jump_Performed;
- InputManager.InputControl.GamePlayer.Jump.canceled -= Jump_Canceled;
- }
-
- private void Start()
- {
- animatorFirstLandingBool = Animator.StringToHash("FirstLanding");
- animatorGroundedBool = Animator.StringToHash("Grounded");
- animatorVelocitySpeed = Animator.StringToHash("Velocity");
- animatorMovementSpeed = Animator.StringToHash("Movement");
- animatorJumpTrigger = Animator.StringToHash("Jump");
- animatorDoubleJumpTrigger = Animator.StringToHash("DoubleJump");
-
- animator.SetBool(animatorFirstLandingBool, firstLanding);
-
- enableGravity = true;
- canMove = true;
- }
-
- private void FixedUpdate()
- {
- UpdateVelocity();
- UpdateJump();
- UpdateDirection();
- UpdateGravityScale();
- }
-
- #endregion
-
- #region Movement
-
- private void UpdateVelocity()
- {
- Vector2 velocity = controllerRigibody.velocity;
- if (vectorInput.x != 0)
- {
- velocity.y = Mathf.Clamp(velocity.y, -maxGravityVelocity / 2, maxGravityVelocity / 2);
- }
- else
- {
- velocity.y = Mathf.Clamp(velocity.y, -maxGravityVelocity, maxGravityVelocity);
- }
- animator.SetFloat(animatorVelocitySpeed, controllerRigibody.velocity.y);
- if (canMove)
- {
- controllerRigibody.velocity = new Vector2(vectorInput.x * maxSpeed, velocity.y);
- animator.SetInteger(animatorMovementSpeed, (int)vectorInput.x);
- }
- }
-
- private void UpdateDirection()
- {
- //控制玩家的旋转
- if (controllerRigibody.velocity.x > 1f && isFacingLeft)
- {
- isFacingLeft = false;
- transform.localScale = flippedScale;
- }
- else if (controllerRigibody.velocity.x < -1f && !isFacingLeft)
- {
- isFacingLeft = true;
- transform.localScale = Vector3.one;
- }
- }
-
- private void UpdateJump()
- {
- if(isJumping && controllerRigibody.velocity.y < 0)
- {
- isFalling = true;
- }
-
- if (JumpInput)
- {
- controllerRigibody.AddForce(new Vector2(0,jumpForce), ForceMode2D.Impulse);
- isJumping = true;
- }
- if(isOnGround && !isJumping && jumpCount != 0) //如果已经落地了,则重置跳跃计数器
- {
- jumpCount = 0;
- counter = Time.time - counter;
- }
- }
-
- private void UpdateGravityScale()
- {
- var gravityScale = groundedGravityScale;
-
- if (!isOnGround)
- {
-
- gravityScale = controllerRigibody.velocity.y > 0.0f ? jumpGravityScale : fallGravityScale;
- }
-
- if (!enableGravity)
- {
- gravityScale = 0;
- }
-
- controllerRigibody.gravityScale = gravityScale;
- }
-
- private void JumpCancel()
- {
- JumpInput = false;
- isJumping = false;
- if(jumpCount == 1)
- {
- animator.ResetTrigger(animatorJumpTrigger);
- }
- else if(jumpCount == 2)
- {
- animator.ResetTrigger(animatorDoubleJumpTrigger);
- }
- }
-
- private void UpdateGrounding(Collision2D collision,bool exitState)
- {
- if (exitState)
- {
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian") || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian"))
- {
- isOnGround = false;
- }
- }
- else
- {
- //判断为落地状态
- if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian")
- || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian")
- && collision.contacts[0].normal == Vector2.up
- && !isOnGround)
- {
- isOnGround = true;
- isJumping = false;
- isFalling = false;
- //effect
- }
- //判断为头顶碰到物体状态
- else if (collision.gameObject.layer == LayerMask.NameToLayer("Terrian") || collision.gameObject.layer == LayerMask.NameToLayer("Soft Terrian")
- && collision.contacts[0].normal == Vector2.down && isJumping)
- {
- JumpCancel();
- }
- }
- animator.SetBool(animatorGroundedBool, isOnGround);
- }
-
- public void StopHorizontalMovement()
- {
- Vector2 velocity = controllerRigibody.velocity;
- velocity.x = 0;
- controllerRigibody.velocity = velocity;
- animator.SetInteger(animatorMovementSpeed, 0);
- }
-
- public void SetIsOnGrounded(bool state)
- {
- isOnGround = state;
- animator.SetBool(animatorGroundedBool, isOnGround);
- }
- #endregion
-
- #region Combat
- private void Jump_Canceled(InputAction.CallbackContext context)
- {
- JumpCancel();
- }
-
- private void Jump_Performed(InputAction.CallbackContext context)
- {
- JumpCancel();
- }
-
- private void Jump_Started(InputAction.CallbackContext context)
- {
-
- counter = Time.time;
- if(jumpCount <= 1)
- {
- ++jumpCount;
- if(jumpCount == 1)
- {
- //Anim+Audio
- animator.SetTrigger(animatorJumpTrigger);
- }
- else if(jumpCount == 2)
- {
- //Anim+Audio+Effect
- animator.SetTrigger(animatorDoubleJumpTrigger);
- }
- }
- else
- {
- return;
- }
- JumpInput = true;
- }
-
- private void OnCollisionEnter2D(Collision2D collision)
- {
- UpdateGrounding(collision, false);
- }
-
- private void OnCollisionStay2D(Collision2D collision)
- {
- UpdateGrounding(collision, false);
- }
-
- private void OnCollisionExit2D(Collision2D collision)
- {
- UpdateGrounding(collision, true);
- }
- #endregion
-
- #region Others
-
- public void FirstLanding()
- {
-
- }
-
- #endregion
-
- }
对于动画我们则要创建一个新的动画状态机名字就叫Jump StateMachine
为我们的Jump,Fall,Soft Land,Double Jump添加好动画
接着就是动画连线了。凡是到Jump和DoubleJump都只用Triiger来作为动画转化条件
回到Base状态机中,Walk,Run,Idle的动画到Jump状态机的动画暂时只有Jump和Fall,而且动画条件也都是一模一样的
除此之外我们还要为动画添加行为脚本,
由此我们先对部分创建好行为脚本。
这些里面大多都是添加音乐和粒子效果所以先不用管,但FallingBehavior则要进行修改
- using UnityEngine;
-
- public class FallingBehavior : StateMachineBehaviour
- {
- float lastPositionY;
- float fallDistance;
- CharacterController2D character;
-
- private void Awake()
- {
- //audio
- character = FindObjectOfType<CharacterController2D>();
- }
-
- // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
- override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- {
- fallDistance = 0;
- animator.SetFloat("FallDistance", fallDistance);
-
- //auido
- }
-
- // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
- override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- {
- if(lastPositionY > character.transform.position.y)
- {
- fallDistance += lastPositionY - character.transform.position.y;
- }
- lastPositionY = character.transform.position.y;
- animator.SetFloat("FallDistance", fallDistance);
- }
-
- // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
- override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
- {
- //audio
- }
-
- public void ResetAllParams()
- {
- lastPositionY = character.transform.position.y;
- fallDistance = 0;
- }
- }
参数先随便设计,设计好后效果如图。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。