Unity下落式音游实现——(4)鼓盘动画及敲击判定

Unity下落式音游实现——(4)鼓盘动画及敲击判定

前期准备

导入资源及布置UI,创建脚本DrumController.cs

思路

由于有多个鼓盘,需要为每个鼓盘创建一个Animator,每个鼓盘有错误敲击和正确敲击两个Animation。另外正确敲击时在鼓盘的上方会有额外的动画,由于美术那边将额外动画和正确敲击的鼓盘分开给我,而且出于复用性考虑,我将额外动画配置鼓盘动画保存为一个prefab,在显示正确动画的同时在对应位置创建(事实证明这是个错误…)。配置动画难度不高,但会有很多很烦人的坑

正常的音游判定思路应该是这样的:滑块与鼓盘的距离小于一定范围时判定敲击成功(可以进一步在范围中细分距离改变敲击等级,good,perfect…),若在范围外敲击鼓盘或滑块到达鼓盘处时仍未被敲击则判定为敲击失败;但该需求中的判定有所不同:滑块到达鼓盘位时并不会销毁,而是在原地待机,若后一个滑块到达鼓盘时上一个滑块仍未销毁,则销毁上一个滑块,敲击时鼓盘处有滑块时判定敲击成功,若无滑块则敲击失败。虽然需求不同,但思路是近似的

实现过程

鼓盘动画

(这部分确实不是很熟悉,如果有错误麻烦指出)

在上一节中我们提到Drum属于UI部分,因此需要创建Canvas,在里面创建好Drum并放在空对象下管理

注意我们还给每个Drum添加一个Audio Source组件,用于播放敲击鼓声
请添加图片描述

//	DrumController.cs
public class DrumController : MonoBehaviour
{
    [SerializeField] private Sprite DrumPic;
    [SerializeField] private Sprite rightDrumPic;
    [SerializeField] private Animator anim;
    [SerializeField] private GameObject RightHitAniPrefab;//    附加动画预置体,显示完删除
    private AudioSource audio;
    
    private GameObject curSlider = null;                //  离终点最近的滑块
    private GameObject secondSlider = null;             //  第二个滑块
}

配置Animator Controller

请添加图片描述

在controller中增加参数ifHit、ifRightHit用于后续敲击判断

注意从NULL过渡到动画状态时需要将Has Exit Time取消勾选,不然的话会出现动画播放太快的问题

配置Animation

大概就是Add Property,然后剩下的慢慢凹吧,太繁琐了,略

tip:你是否经历过做好Animation后想预览想过,却发现Preview那栏的播放键点不开,那是因为你可能直接在Project的资源文件夹里打开Animation了,但预览需要在Hiearchy选中(左键单击Animation所在的gameobject上)所以正确的预览动画方式应该是先打开Animation窗口,选中对应gameobject,最后在preview下方选取你想要看的Animation

接受击鼓信号

我们上一节实现了观察者模式,可以在DrumController中使用,监听击鼓消息

    private void Awake()
    {
        //	接受一个编号,在hitDrum判断是不是自己被敲
       Messenger<int>.AddListener(GameEvent.DRUM_HIT, hitDrum);
    }

    private void OnDestroy()
    {
        Messenger<int>.RemoveListener(GameEvent.DRUM_HIT, hitDrum);
    }

实现hitDrum

//	判断敲击是否正确   
bool judgeHit()
    {
        if (curSlider)
        {
            bool t = curSlider.GetComponent<SliderController>().judgeHit();
            //  击鼓正确则销毁
            if (t)
                Destroy(curSlider);
            return t;
        }
        else
            return false;
    }

void hitDrum(int durmNum)
    {
        //  show状态不能击鼓
        if (("Drum" + durmNum) == name)
        {
            audio.Play();
            if (status == MissionStatus.game)
            {
                bool t = judgeHit();
                anim.SetBool("ifHit", true);
                anim.SetBool("ifRightHit", t);
                if (t)
                {
                    //	生成额外动画
                    GameObject rightHitAni = Instantiate(RightHitAniPrefab, transform) as GameObject;
                    rightHitAni.transform.position = new Vector3(transform.position.x + 30, transform.position.y + 300, transform.position.z);
                    StartCoroutine(DestoryAni(rightHitAni));
                }
            }
        }
    }

关于生成额外动画部分,我们在鼓盘附近生成了预先配置好的prefab,动画自动播放,然后用协程在播放完之后删除

敲击判定

SenceController需要在生成滑块时通知对应的drum

//	SenceController.cs
private Transform[] Drums;

void Start()
{
    ...
    Drums = Drum.GetComponentsInChildren<Transform>();
}
void genSlider(int OrbitNum)
{
    ...
    //  设置slider初始条件
    ...
    //  传给鼓,用于判断正确率和销毁滑块
    Drums[OrbitNum + 1].SendMessage("setNewSlider", slider);    
}

在DrumController更新滑块信息,判断是否销毁滑块。回忆一下我们的需求,在展示阶段(show)滑块可视且碰到鼓盘后销毁,在游戏阶段(game)滑块不可视且要根据轨道上的其他滑块来判定是否销毁当前滑块,这就意味着鼓盘和滑块不能解耦,滑块不能单独决定是否销毁自身,必须从鼓盘处获得信息(是否有上一个滑块?)

我们给每个鼓盘维护一个队列,记录在这个轨道上的滑块,并在状态切换时清除所有还留在鼓盘处的滑块

//  被SenceController调用
    void setNewSlider(GameObject slider)
    {
        //  如果这条轨道上之前有滑块,就入队列
        if (curSlider == null)
            curSlider = slider;
        else
             sliderPool.Enqueue(slider);
    }

    void ifDestoryCurrentSlider()
    {
        if (curSlider)
            if (curSlider.GetComponent<SliderController>().status == MissionStatus.show)
            {
                if (curSlider.GetComponent<SliderController>().ifDestory)
                {
                    Destroy(curSlider);
                    if (sliderPool.Count > 0)
                        curSlider = sliderPool.Dequeue();
                    showRightDrumPic();
                }
            }
            else
            {
                if (sliderPool.Count > 0)
                {
                    //  如果下一个滑块到了,那就销毁前一个滑块
                    if (Vector3.Distance(curSlider.transform.position,sliderPool.Peek().transform.position)<0.001f)
                    {
                        Destroy(curSlider);
                        curSlider = sliderPool.Dequeue();
                    }
                }
            }
    }

void Update()
{
    ifDestoryCurrentSlider();
}

Q:在Update里调用ifDestoryCurrentSlider()会不会有点浪费,毕竟只有在敲鼓或第二个滑块出现时才有可能会销毁

A:你说得对,但是没必要。而且滑块出现频率还挺高,优化不了多少

注意到在ifDestoryCurrentSlider函数中我们调用了curSlider.GetComponent().ifDestory,打开SliderController

//	SliderController.cs
public bool ifDestory = false;

private void FixedUpdate()
{
    //	移动滑块
    ...
        //  game状态下销毁交给DrumController处理
        //  show状态下自己通知DrumController销毁
        if (status == MissionStatus.show && targetIndex == target.Length)
        {
            ifDestory = true;
        }
}

Q:为什么不能在DrumController中(或者让slider自己)统一处理slider销毁?

A:从设计上slider和Drum是分离的(slider在gameobject层,Drum在UI层),但是需求中game状态下slider的销毁不能单独完成,这就破坏了分离。如果是传统音游,就直接全部在DrumController解决就好

总结

我们现在已经能生成滑块,敲击鼓盘并根据滑块信息判断敲击是否合法,显示对应的动画。接下来实现如何“正确地生成滑块”

在上述部分中省略了部分不重要的信息,如怎么样接受输入等,这一部分暂时是用键盘接受输入,可以自己实现(从硬件处接受输入的方案后续会提)

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity是一款强大的戏开发引擎,可以用于制作各种类型的戏,包括戏。下面是使用Unity制作的一般步骤: 1. 创建项目:在Unity中创建新的项目,并选择一个适合的项目名称和存储位置。 2. 导入资源:收集或创建戏所需的频资源和图形资源,并将其导入到Unity项目中。可以选择不同类型的效和乐,并确保它们在项目中正确加载和播放。 3. 设计戏场景:设计戏主界面和不同的戏关卡,包括戏背景、按钮、角色等。可以使用Unity的内置可视化编辑器来创建和布置戏场景。 4. 编写脚本:使用Unity提供的脚本编辑器编写戏逻辑脚本。可以编写处理符生成、点击判定、背景特效等功能的脚本。了解C#或Unity脚本语言将对此步骤有所帮助。 5. 添加效和动画:使用Unity动画编辑器创建角色动画效果,并将效与戏逻辑脚本链接起来。例如,点击符时播放声效果,或者当播放乐时触发特定的戏事件。 6. 进行戏测试:在Unity中进行戏测试,确保戏的效、动画和玩法等方面都符合预期。进行测试并修复可能存在的漏洞和问题,直到戏可以正常运行。 7. 发布戏:根据目标平台(如PC、移动设备等),选择合适的发布选项进行戏的发布。可以将戏导出为可执行文件、移动应用程序等格,以供玩家下载和安装。 Unity开发者提供了强大的工具和资源,使其能够创建出令人兴奋和有吸引力的戏。这些步骤只是制作的基本流程,具体的开发过程还需根据实际情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值