Unity小游戏——使被砍中的怪物四处飞散

简介: Unity小游戏——使被砍中的怪物四处飞散

被武士砍中后,怪物将向四面八方飞散。

动作的不同将导致攻击力度的强弱表现不同,被攻击的各个对象的反应也有很大差异。在格斗游戏中,对对手一顿拳打脚踢后,看到其步履蹒跚的样子,往往可以感受到他的疼痛。相反如果对手显得从容不迫,即使动作再华丽也只能给人一种给攻击力很弱的印象。

有时候我们常常听到攻击反馈的说法。在玩游戏时大家应该都有过感觉按键和摇杆好像变重了的经历吧?可以说这种游戏通过通过视觉和听觉把攻击反馈完美地呈现了出来。

我们将通过怪物的四处飞散来表现武士的攻击强度。另外,我们也将实现上文提到的靠近斩杀怪物会获得高分的规则,并使“在多金的距离斩杀了怪物”影响怪物的飞散方式。

不过每次都采用同样的飞散方式未免有些单调,因此我们会调整飞散的方向使每次的效果都略有不同。


一、想象一下“圆锥体”

在考虑实现方法之前,我们首先整理一下“需要做什么”,用专业术语来说在这叫需求分析

  • 要让怪物华丽地四处飞散
  • 让每次的动作都各有不同

“华丽”这种描述对于变成来说是一个比较暧昧的说法,应该描述得更加具体一些。

之前已经提到过,把若干个怪物编成一个小组,并通过这个小组来执行被攻击判定。受到攻击时小组内的所有怪物都将四处飞散。而如果怪物们都向着同样的方向飞去,将毫无“华丽”可言。换句话说,所谓的“华丽”,应该是这些怪物尽量朝着不同的方向飞散开来。

这个被刀砍中后各自飞散的过程,更类似于炸弹爆炸的画面。由于怪物被刀砍中时受到了某一方向的作用力,因此往相反的一侧飞出才显得自然。武士具有右斩、左斩的动作,每个动作都将令怪物反方向飞出。

“靠近斩杀时怪物将更华丽地飞散开”这个要素也是必要的。虽然单纯改变速度也能达到类似的效果,但为了让玩家更容易地了解是否完美地看中了怪物,我们将飞散的方向改为前后方向。如果从前面飞来的怪物都按照相同的方向弹开,就能让玩家强烈地感受到攻击的力度。

那么我们再次细化需要完成的工作:

  • 怪物朝不同的方向飞出
  • 根据动作的不同往左或往右飞出
  • 根据斩杀时的距离远近调整为前后
  • 每次飞出的方式都有变化

要是每次飞出的方式都不一样,很多人会想到使用随机数。不过如果仅对飞出的方向和速度进行随机化处理,虽然可以改变飞出的方向,但不保证怪物会按照我们期待的方向飞出。

像这样“想在随机化的同时进行某种程度的倾向控制”的时候,解决问题的关键就是先确定好关键性的原则,再使用随机数改变细节参数。

这里我们参考水管喷头喷水的情景,决定使怪物沿着圆锥的表面飞出,也就是说,圆锥的朝向基本上决定了飞散的方向,底面的半径则决定了飞散开的范围。

二、具体的计算方法

接下来,我们对各个参数进行详细的说明。

首先看看圆锥的底面半径如何决定了飞散的范围

怪物被砍中后飞出的方向是由武士攻击瞬间的速度向量决定的。所有怪物被击飞后的速度向量都以圆锥的顶点为起始点,终点位于圆锥底面的圆周上,按照一定间隔并列排开。

底面半径越大圆锥的开口范围越广,每个怪物的速度向量的方向也有很大差异,因此怪物飞散范围就比较广。反之如果半径比较小,则飞散开的范围就比较窄。

下面,我们通过圆锥的倾角来控制前后方向

这里的“前后”,指的是从武士的视角看到的前后。武士向画面右方前进,也就是+X方向,这样在画面上看起来就是左右倾斜。需要注意的是在计算时会变为围绕Z轴旋转。

最后,通过圆弧的中心角度来控制左右方向的飞散

怪物飞散的方向,也就是速度向量分布在圆锥的表面上。但它们并没有完全分布在1周360°的各个角落,而是集中在了大约半个圆周的范围内。这里将通过排列着的各个速度向量的圆弧的中心点的角度控制左右方向。程序中使用y_angle_swing变量来表示。

下面我们结合代码来看看实际的计算过程:OniGroupControl.OnAttackedFromPlayer方法

public void OnAttackedFromPlayer()
  {
    // 累加被击倒的怪物数量
    // (后续部分也会进行计算评价,不过这里先执行一次)
    this.scene_control.AddDefeatNum(this.oni_num);
    // 怪物向四处飞散
    //
    // 在圆锥表面的形状上决定各个怪物飞散开的方向
    // 评价越高则圆锥的开口越大,这样就能飞散到更广的区域
    // 玩家的速度如果较快,圆锥会向前倾斜一些
    Vector3     blowout;        // 怪物飞散的方向(速度向量)
    Vector3     blowout_up;       // ↑的垂直分量
    Vector3     blowout_xz;       // ↑的水平分量
    float     y_angle;
    float       blowout_speed;
    float     blowout_speed_base;
    float     forward_back_angle;   // 圆锥的前后倾斜角度
    float     base_radius;      // 圆锥的地面半径
    float     y_angle_center;
    float     y_angle_swing;      // 圆弧的中心(根据动作左右决定该值)
    float     arc_length;       // 圆弧的长度(圆周)
    switch(this.scene_control.evaluation) {
      default:
      case SceneControl.EVALUATION.OKAY:
      {
        base_radius = 0.3f;
        blowout_speed_base = 10.0f;
        forward_back_angle = 40.0f;
        y_angle_center = 180.0f;
        y_angle_swing  = 10.0f;
      }
      break;
      case SceneControl.EVALUATION.GOOD:
      {
        base_radius = 0.3f;
        blowout_speed_base = 10.0f;
        forward_back_angle = 0.0f;
        y_angle_center = 0.0f;
        y_angle_swing = 60.0f;
      }
      break;
      case SceneControl.EVALUATION.GREAT:
      {
        base_radius = 0.5f;
        blowout_speed_base = 15.0f;
        forward_back_angle = -20.0f;
        y_angle_center = 0.0f;
        y_angle_swing = 30.0f;
      }
      break;
    }
    forward_back_angle += Random.Range(-5.0f, 5.0f);
    arc_length = (this.onis.Length - 1)*30.0f;
    arc_length = Mathf.Min(arc_length, 120.0f);
    // 根据玩家的动作(左斩,右斩),改变左右飞散的方向
    y_angle = y_angle_center;
    y_angle += -arc_length/2.0f;
    if(this.player.attack_motion == PlayerControl.ATTACK_MOTION.RIGHT) {
      y_angle += y_angle_swing;
    } else {
      y_angle -= y_angle_swing;
    }
    y_angle += ((OniGroupControl.count*7)%11)*3.0f;
    // 让组内的怪物全部被击倒
    foreach(OniControl oni in this.onis) {
      //
      blowout_up = Vector3.up;
      blowout_xz = Vector3.right*base_radius;
      blowout_xz = Quaternion.AngleAxis(y_angle, Vector3.up)*blowout_xz;
      blowout = blowout_up + blowout_xz;
      blowout.Normalize();
      // 圆周向前后倾斜
      blowout = Quaternion.AngleAxis(forward_back_angle, Vector3.forward)*blowout;
      // 飞散开的速度
      blowout_speed = blowout_speed_base*Random.Range(0.8f, 1.2f);
      blowout *= blowout_speed;
      if(!SceneControl.IS_ONI_BLOWOUT_CAMERA_LOCAL) {
        // 全局坐标系下飞散开(不和摄像机发生连动)时,
        // 要加上玩家的速度
        blowout += this.player.GetComponent<Rigidbody>().velocity;
      }
      // 旋转
      Vector3 angular_velocity = Vector3.Cross(Vector3.up, blowout);
      angular_velocity.Normalize();
      angular_velocity *= 3.14f*8.0f*blowout_speed/15.0f*Random.Range(0.5f, 1.5f);
      //angular_velocity = Quaternion.AngleAxis(Random.Range(-30.0f, 30.0f), Vector3.up)*angular_velocity;
      //
      oni.AttackedFromPlayer(blowout, angular_velocity);
      //Debug.DrawRay(this.transform.position, blowout*2.0f, Color.white, 1000.0f);
      //
      y_angle += arc_length/(this.onis.Length - 1);
    }
    // 播放被击倒的音效
    // 太多的音效同时播放不容易听清,只播放一个
    //
    if(this.onis.Length > 0)
    {
      AudioClip[] yarareSE = null;
      if( this.onis.Length >= 1 && this.onis.Length < 3 )
      {
        yarareSE = this.YarareLevel1;
      }
      else if( this.onis.Length >= 3 && this.onis.Length < 8 )
      {
        yarareSE = this.YarareLevel2;
      }
      else if( this.onis.Length >= 8 )
      {
        yarareSE = this.YarareLevel3;
      }
      if( yarareSE != null )
      {
        int index = Random.Range( 0, yarareSE.Length );
        this.onis[0].GetComponent<AudioSource>().clip = yarareSE[index];
        this.onis[0].GetComponent<AudioSource>().Play();
      }
    }
    OniGroupControl.count++;
    // 删除实例
    //
    // 执行Destroy(this) 后, 删除的不是OniGroupPrefab 实例,而是脚本(OniGroupControl)
    // 请注意
    //
    Destroy(this.gameObject);
  }


相关文章
|
5月前
|
图形学
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
170 0
|
11月前
|
图形学
Unity小游戏——迷你拼图
Unity小游戏——迷你拼图
245 1
|
11月前
|
图形学
unity小游戏——得分高低的判定
unity小游戏——得分高低的判定
|
11月前
|
机器学习/深度学习 算法 图形学
Unity小游戏——无限滚动的背景的改良
Unity小游戏——无限滚动的背景的改良
105 0
|
11月前
|
算法 图形学
Unity小游戏——武士击杀小怪兽(无限滚动的背景)
Unity小游戏——武士击杀小怪兽(无限滚动的背景)
|
6月前
|
图形学
【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏
【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏
|
11月前
|
图形学
Unity小游戏——武士和怪物的碰撞检测
Unity小游戏——武士和怪物的碰撞检测
|
11月前
|
图形学
Unity小游戏——怪物出现模式的管理
Unity小游戏——怪物出现模式的管理
123 0
|
图形学
Unity实现2D小游戏
Unity实现2D小游戏FirstGame2D(Sunny Land) 一、游戏说明 本游戏为作者的第一个实验的2D小游戏 实现效果: (1)简单的UI界面以及触发按钮、滑动按钮事件 (2)通过按钮与按键实现场景的切换 (3)通过代码实现动画效果的切换(跳跃、蹲下等) (4)碰撞体以及触发器实现消灭敌人、收集物品、地面检测 (5)相机场景与背景运动差,实现场景与人物的立体效果,达到较好的视觉效果 (6)SoundMananger实现场景音乐的管理
256 0
Unity实现2D小游戏
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
173 6