文章目录
- 前言
- 人物节点创建
- 实现简单移动
- 实现攻击效果
- 实现跳跃
- 人物移动跳跃完整代码
- 人物脚底的影子效果
- 最终运行效果
- 源码
- 参考
- 完结
前言
之前有个小伙伴微信找我,想做一个类似DNF地下城勇士的移动跳跃功能,特别是关于2d的跳跃,之前还不是很有头绪,后面去查找相关资料,还真让我找到了,今天就一起来实现一下吧!
实现类似2.5d的地下城勇士效果,正常来说2d 3d都可以做,但是我还是选择2d,2d操作起来更为简单,而且素材也容易寻找。
先来看看实现的最终效果吧
人物节点创建
Player挂载刚体和碰撞器,主要控制主角移动
Mbody为人物身体,挂载animator组件,主要控制主角动画切换
Shadow为阴影
实现简单移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerParentMove : MonoBehaviour
{
public float moveSpeed = 10;//移动速度(父物体)
private Vector2 direction;//移动方向(父物体,xy轴平面)
private Animator ani;//动画控制器(子物体)
private Rigidbody2D rig;//刚体组件(父物体)
void Start()
{
ani = GetComponentInChildren<Animator>();//初始化获得子物体的动画控制器
rig = GetComponent<Rigidbody2D>();//初始化获得自身的刚体组件
}
void Update()
{
direction.x = Input.GetAxisRaw("Horizontal");//获取键盘x轴输入,赋值给移动方向的x值
direction.y = Input.GetAxisRaw("Vertical") * 0.7f;//获取键盘x轴输入,赋值给移动方向的x值(*0.7是因为视觉效果,向里走要慢一点,可根据实际效果改变)
}
//之所以在FixUpdate里面执行移动和跳跃方法,是因为父物体移动用的是刚体的Velocity方法,子物体跳跃虽然是用Transform,但是为了跳跃时配合移动不出现卡顿,也放在FixUpdate里面执行
private void FixedUpdate()
{
Move();//移动方法(父物体)
Anima();//执行动画控制方法
}
void Move()
{
rig.velocity = direction * moveSpeed * 50 * Time.fixedDeltaTime;//父物体沿移动方向移动
if (rig.velocity.x >= 0.05)
{
transform.rotation = new Quaternion(0, 0, 0, 0);//如果速度向右不翻转
}
else if (rig.velocity.x <= -0.05)
{
transform.rotation = new Quaternion(0, 180, 0, 0); //如果速度向左翻转180°
}
}
void Anima() {}
}
效果
实现攻击效果
public bool isAttack = false;//攻击状态
void Update()
{
//检测键盘攻击键输入并且攻击状态为false
if(Input.GetMouseButtonDown(0) && !isAttack)
{
isAttack = true;//攻击状态赋值为true
Accatk();
}
}
private void FixedUpdate()
{
if (isAttack) direction = new Vector2(0, 0);
}
void Accatk() {}
效果
实现跳跃
public float jumpHeight = 5;//跳跃高度
public float aSpeed = -9.8f;//重力加速度
private bool isJump = false;//跳跃状态
private float velocity_Y;//跳跃速度(子物体)
public Transform childTransform;//Transform组件(子物体)
void Update()
{
//检测键盘跳跃键输入并且跳跃状态为false
if (Input.GetKeyDown(KeyCode.Space) && !isJump &&!isAttack)
{
isJump = true;//跳跃状态赋值为true
ReadyJump();//执行准备跳跃方法
}
}
//之所以在FixUpdate里面执行移动和跳跃方法,是因为父物体移动用的是刚体的Velocity方法,子物体跳跃虽然是用Transform,但是为了跳跃时配合移动不出现卡顿,也放在FixUpdate里面执行
private void FixedUpdate()
{
Jump();//跳跃方法(子物体)
}
void Jump()
{
velocity_Y += aSpeed * Time.fixedDeltaTime;//重力模拟(子物体垂直速度始终受重力加速度影响)
//判断子物体是在下落状态(velocity小于零)并且距离父物体小于等于0.05
if (childTransform.position.y <= transform.position.y + 0.05f && velocity_Y < 0)
{
//如果满足
velocity_Y = 0;// 子物体垂直速度清零
childTransform.position = transform.position;//子物体position与父物体对齐
//检测是否对齐,理论上多此一举,但是之前有遇到过位置不准确的情况,所以加一个双保险
if (childTransform.position == transform.position)
{
//满足位置对齐
isJump = false;//则将跳跃状态设置为false,等待下一次跳跃
}
}
childTransform.Translate(new Vector3(0, velocity_Y) * Time.fixedDeltaTime);//子物体按照速度移动
}
void ReadyJump()
{
velocity_Y = Mathf.Sqrt(jumpHeight * -2f * aSpeed);
}
效果
人物移动跳跃完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerParentMove : MonoBehaviour
{
public float moveSpeed = 10;//移动速度(父物体)
public float jumpHeight = 5;//跳跃高度
public float aSpeed = -9.8f;//重力加速度
private Vector2 direction;//移动方向(父物体,xy轴平面)
private bool isJump = false;//跳跃状态
public bool isAttack = false;//攻击状态
private float velocity_Y;//跳跃速度(子物体)
private Animator ani;//动画控制器(子物体)
private Rigidbody2D rig;//刚体组件(父物体)
public Transform childTransform;//Transform组件(子物体)
void Start()
{
ani = GetComponentInChildren<Animator>();//初始化获得子物体的动画控制器
rig = GetComponent<Rigidbody2D>();//初始化获得自身的刚体组件
}
// Update is called once per frame
void Update()
{
direction.x = Input.GetAxisRaw("Horizontal");//获取键盘x轴输入,赋值给移动方向的x值
direction.y = Input.GetAxisRaw("Vertical") * 0.7f;//获取键盘x轴输入,赋值给移动方向的x值(*0.7是因为视觉效果,向里走要慢一点,可根据实际效果改变)
//检测键盘跳跃键输入并且跳跃状态为false
if (Input.GetKeyDown(KeyCode.Space) && !isJump &&!isAttack)
{
isJump = true;//跳跃状态赋值为true
ReadyJump();//执行准备跳跃方法
}
//检测键盘攻击键输入并且攻击状态为false
if(Input.GetMouseButtonDown(0) && !isAttack && !isJump)
{
isAttack = true;//攻击状态赋值为true
//Accatk();
}
}
//之所以在FixUpdate里面执行移动和跳跃方法,是因为父物体移动用的是刚体的Velocity方法,子物体跳跃虽然是用Transform,但是为了跳跃时配合移动不出现卡顿,也放在FixUpdate里面执行
private void FixedUpdate()
{
if (isAttack) direction = new Vector2(0, 0);
Move();//移动方法(父物体)
Jump();//跳跃方法(子物体)
Anima();//执行动画控制方法
}
void Move()
{
rig.velocity = direction * moveSpeed * 50 * Time.fixedDeltaTime;//父物体沿移动方向移动
if (rig.velocity.x >= 0.05)
{
transform.rotation = new Quaternion(0, 0, 0, 0);//如果速度向右不翻转
}
else if (rig.velocity.x <= -0.05)
{
transform.rotation = new Quaternion(0, 180, 0, 0); //如果速度向左翻转180°
}
}
void Jump()
{
velocity_Y += aSpeed * Time.fixedDeltaTime;//重力模拟(子物体垂直速度始终受重力加速度影响)
//判断子物体是在下落状态(velocity小于零)并且距离父物体小于等于0.05
if (childTransform.position.y <= transform.position.y + 0.05f && velocity_Y < 0)
{
//如果满足
velocity_Y = 0;// 子物体垂直速度清零
childTransform.position = transform.position;//子物体position与父物体对齐
//检测是否对齐,理论上多此一举,但是之前有遇到过位置不准确的情况,所以加一个双保险
if (childTransform.position == transform.position)
{
//满足位置对齐
isJump = false;//则将跳跃状态设置为false,等待下一次跳跃
}
}
childTransform.Translate(new Vector3(0, velocity_Y) * Time.fixedDeltaTime);//子物体按照速度移动
}
void ReadyJump()
{
velocity_Y = Mathf.Sqrt(jumpHeight * -2f * aSpeed);
}
void Anima()
{
ani.SetFloat("Velocity", velocity_Y);
ani.SetBool("isJump", isJump);
if(!isJump)
{
if (Mathf.Abs(rig.velocity.x) >= 0.05 || Mathf.Abs(rig.velocity.y) >= 0.05)
{
ani.SetBool("Running", true);
}
else ani.SetBool("Running", false);
}
else
{
ani.SetBool("Running", false);
}
if(isAttack)
{
ani.SetBool("isAttack", true);
}
}
}
挂载脚本和参数配置
人物脚底的影子效果
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShadow : MonoBehaviour
{
public Transform playerTranform;//玩家(子物体)Transform
public float shadowSizeFloat = 0.5f;//影子最小缩放比例(在原有基础上)
private float heightDifference;//玩家跳跃高度差
private Vector3 scale;//初始影子缩放大小
private PlayerParentMove playerMove;//声明玩家(父物体)移动脚本,主要是获取玩家设置的跳跃高度
void Start()
{
playerMove = GetComponentInParent<PlayerParentMove>();//得到玩家(父物体)移动脚本
scale = transform.localScale;//将影子初始缩放赋值给scale
}
void Update()
{
heightDifference = playerTranform.position.y - transform.position.y;//高度差计算:子物体y值-父物体y值
//按照最大跳跃高度和高度差的比例来缩放影子大小,限制影子最小缩放
//Mathf.Clamp()这里有三个参数,第一个参数是要限制的变量,第二个是最小值,第三个是最大值
//用scale.x-(heightDifference/playerMove.jumpHeight)*scale.x,计算根据高度差与最大高度比例从0到初始值的变换
//并且在最小值使用scale.x*shadowSizeFloat,来限制最小值,即使计算出来是0小于最小值,返回值也会是最小值
//scale.y缩放同理
transform.localScale = new Vector3(Mathf.Clamp(scale.x-(heightDifference/playerMove.jumpHeight)*scale.x, scale.x * shadowSizeFloat, scale.x), Mathf.Clamp(scale.y - (heightDifference / playerMove.jumpHeight) * scale.y, scale.y * shadowSizeFloat, scale.y), scale.z);
}
}
脚本挂载在人物Shadow上,并配置参数
运行效果
最终运行效果
源码
链接:https://pan.baidu.com/s/1LL8gEhjeEHLMXzWXQayFwQ
提取码:zcq9
参考
【视频】https://www.bilibili.com/video/BV1EE411p7iC/
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~