怎样制作一个回旋斧 2D篇
【参考视频】
url:bilibili - 【中文字幕】在2D中重现:战神4奎爷投掷和召回寒冰之斧的攻击方式
这个视频中不仅实现了回旋斧的制作,还实现了相机震动、以及一些特效效果丰富体验。有兴趣的小伙伴可以观看这个视频,这里我只实现普通的回旋斧制作。
【PS:我实现的方式与这位UP有些不同,若是有错误请指正】
一、打开初始资源
下载地址 https://pan.baidu.com/s/1L5APoVJundN1IUDphSNUBg
提取码:v4m2
- 打开Scenes文件夹下的第一个场景,这个场景已经实现了部分功能(玩家移动,敌人攻击,按空格敌人血量会减少)。
- 打开Sprites文件夹,找到weapon斧头,将其拖拽到Player的子物体中,并调整Position和Sorting Layer以显示在正确的位置
二、在Scripts/01文件夹下新建C#脚本 Weapon
(1)首先来实现物体的旋转,在点击斧头物体按E进行旋转可知斧头是围绕Z轴进行旋转的。简单的旋转只需以下代码即可:
public class Weapon: MonoBehaviour
{
[Header("旋转参数")]
[SerializeField]
float rotateSpeed = 1000f; // 旋转速度
private void Update()
{
SelfRotate();
}
void SelfRotate()
{
// 斧头围绕Z轴顺时针旋转
transform.Rotate(-Vector3.forward * rotateSpeed * Time.deltaTime);
}
}
(2)接下来实现鼠标点击某个位置,斧头会旋转并移动到该位置。
新建两个变量 移动速度moveSpeed , 目标位置 targetPos
[Header("移动参数")]
[SerializeField]
float moveSpeed = 10f; // 移动速度
Vector2 targetPos; // 鼠标点击位置
private void Update()
{
SelfRotate();
Movement();
// 点击鼠标左键
if (Input.GetMouseButtonDown(0))
{
targetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
}
void Movement()
{
// 斧头以一定速度移动到目标位置
transform.position = Vector2.MoveTowards(transform.position, targetPos, moveSpeed * Time.fixedDeltaTime);
}
(3)接下来实现武器到达目标点位置后,返回人物手中。
首先声明几个变量, 玩家位置 playerPos, 三个bool值 isRotating 旋转状态, isGo 前进状态, isBack 返回状态。
逻辑:
·点击鼠标时,武器开始旋转,斧头移动到鼠标点击位置(即 isGo = true)。·
·斧头到达目标位置后,开始返回到玩家位置playerPos(即 isGo = false,isBack = true)
Vector2 playerPos; // 玩家位置
[Header("bool参数")]
[SerializeField]
bool isRotating; // 旋转状态
[SerializeField]
bool isGo; // 前进状态
[SerializeField]
bool isBack; // 返回状态
private void Update()
{
playerPos = transform.parent.position;
// 点击鼠标左键 && 斧头是静止状态
if (Input.GetMouseButtonDown(0) && !isGo && !isBack)
{
isRotating = true; // 开始旋转
isGo = true; // 开始前进
targetPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
if (isRotating)
{
SelfRotate();
Movement();
}
}
void Movement()
{
// 如果武器到达了目标点
if (Vector2.Distance(transform.position, targetPos) < 0.01f)
{
isBack = true;
isGo = false;
}
// 如果武器返回玩家手中
if(Vector2.Distance(transform.position, playerPos) < 0.01f)
{
isBack = false;
}
// 武器处于前进状态 且 不是返回状态
if (isGo && !isBack)
{
// 斧头以一定速度移动到目标位置
transform.position = Vector2.MoveTowards(transform.position, targetPos, moveSpeed * Time.fixedDeltaTime);
}
// 武器处于返回状态 且 不是前进状态
if (isBack && !isGo)
{
// 斧头以一定速度移动到目标位置
transform.position = Vector2.MoveTowards(transform.position, playerPos, moveSpeed * Time.fixedDeltaTime);
}
}
(4)这时候斧头返回玩家手中时并不会停止旋转,但是,如果用Vector2.Distance()的方法判断斧头是否回到玩家的位置,然后赋值isRotating = false。就会出现错误了!因为武器一直在玩家的位置,所以isRotating会一直会false,根本不会旋转和移动了。
// 错误的方法
if (Vector2.Distance(transform.position, playerPos) < 0.01f)
{
isRotating = false;
isBack = false;
}
所以我们要添加另外的条件,因为斧头返回状态中isBack是为true的,所以我们可以判断 isBack为true 且 斧头回到玩家位置时, 斧头停止旋转,返回状态为假,这样这个方法就只在 武器回到玩家手中 的时候调用了一次,而不是无限调用
// 正确的方法
if (isBack && Vector2.Distance(transform.position, playerPos) < 0.01f)
{
isRotating = false;
isBack = false;
// 返回初始的旋转角度
transform.rotation = Quaternion.Euler(0, 0, 0);
}
(5)两个缺陷:
1.我们注意到,武器回到的位置是玩家中心的位置,而不是回到玩家手上
2.因为武器是玩家的子物体,所以武器会跟随玩家的移动而移动(也就是说玩家的速度会影响到武器的速度),武器和玩家同时运动就会会显得别扭,如果玩家移动速度过快,武器甚至会反向运动。 .
有一种方法可以同时解决这两个缺陷,就是将武器的位置替代为一个空物体,然后将武器移出Player作为一个单独的物体,然后改变脚本中的一些参数即可
1.在Player下创建一个空物体weapon_point,并将斧头移动到Player外作为独立的物体
2.进入脚本修改一些代码
删掉变量 playerPos,声明一个公共变量Transform point,删掉Update()中对 playerPos 的赋值,然后将所有的playerPos改为point.position即可
不要忘记在unity中对point进行赋值
3.这样武器确实不会跟随玩家进行移动了,但是产生了新bug,武器在玩家手中的时候也不会跟随玩家移动了。所以我定义了一个bool值,onHand,默认为true,用来判断武器是否回到玩家手中。
在Update()方法中,当点击鼠标左键时,物体会进行移动,所以此时onHand应为false
在Update()方法中,当onHand = true时,让武器的位置一直为点的位置
在Movement()方法中,当武器回到手中时,onHand应为true
[SerializeField]
bool onHand = true; // 在手中
// 在Update中添加
private void Update()
{
// 点击鼠标左键 && 斧头是静止状态
if (Input.GetMouseButtonDown(0) && !isGo && !isBack)
{
onHand = false;
}
if (onHand)
transform.position = point.position;
}
void Movement()
{
// 如果武器返回玩家手中
if (isBack && Vector2.Distance(transform.position, point.position) < 0.01f)
{
onHand = true;
}
}
【PS:这种方法只是相对减少了一些违和感,并不能完全阻止,比如当玩家的移动速度快于斧头的移动速度,如果斧头返回图中玩家也一起移动的话,斧头就会“追”不上玩家,所以还需合理的限制玩家和斧头的速度】
(6)实现攻击
只需给敌人添加collider 2D组件,给斧头添加collider 2D(勾选is trigger),rigidbody 2D(关掉重力)组件,然后用OntriggerEnter2D方法即可
1.在Weapon脚本中新增方法
注意:任何判断碰撞的方法都需要双方物体都有collider组件,且其中一个物体带有rigidbody组件
private void OnTriggerEnter2D(Collider2D collision)
{
// 调用Enemy的脚本中的Hurt方法,参数为25
if (collision.CompareTag("Enemy"))
collision.gameObject.SendMessage("Hurt", 25);
}
别忘记给Enemy添加tag Enemy,如果没有请点击Add Tag新增
2.打开Enemy脚本,根据写好的参数直接定义Hurt方法
public void Hurt(int damage)
{
GetComponentInChildren<HealthBar>().hp -= damage;
}
实现效果
·完整项目百度云
https://pan.baidu.com/s/1Kkrx8v5MRfHWNPz0RVKFtQ
提取码:zfet
Unity版本:2019.4.3及以上
日期:2020-10-24