Unity3D 节奏游戏 编辑器制谱工具

在之前的项目中有遇到过负责开发节奏游戏的需求,查阅网上相关资料后了解了关于音乐游戏的制作核心(节点谱),但是网上目前基于Unity3D的音乐游戏还较匮乏,仅有的DEMO甚至还在使用PartieEmitter所以处于无法运行的状态,于是参考DEMO(Guitar Hero)的编辑器部分配合配合反射获取的API实现了一个编辑器制谱工具。

:该篇由于涉及到多个层面(反射/编辑器GUI交互/ScriptableObject)导致代码量过多 所以将项目DEMO传到GITHUB上,地址在最下文。

个人需求

节奏游戏的制谱工具,能够在编辑器环境下设定BPM,Offset,节点并对当前音乐进行播放停止等操作.

实现效果

设定获取BPM与Offset后,对节拍点击即可给选定节拍绑定制定类型的节点.
Unity3D Rhythm Node Creator

思路原理

节点数存储:使用ScriptableObject作为节点数据存储容器.
编辑器播放音乐:反射Internall类方法供编辑器GUI调用.
节点数据调整:使用编辑器GUI交互对ScriptableObject内数据进行调整.

代码实现

节点存储类

思路:使用ScriptableObject储存节点,与目标音乐信息

using System.Collections.Generic;
using UnityEngine;
public enum enum_BeatType
{
   
    Invalid = -1,
    Single = 1,
    Double = 2,
    Triple = 3,
}
[System.Serializable]
public class Node		//节点
{
   
    public Node(int beatPos, bool isLeft,enum_BeatType type)
    {
   
        i_BeatPos = beatPos;
        b_IsLeft = isLeft;
        e_Type = type;
    }
    public int i_BeatPos;
    public bool b_IsLeft;
    public enum_BeatType e_Type;
}
[CreateAssetMenu(fileName = "Nodes_", menuName = "BeatsNodes")]
public class BeatNodes : ScriptableObject
{
   
    [SerializeField]    
    List<Node> l_Nodes = new List<Node>();
    public int  I_BeatPerMinute;
    public float F_BeatStartOffset;
    public AudioClip AC_ClipToPlay;
    Node tempNode;
    
    #region Interact APIs
    public void Clear()
    {
   
        l_Nodes.Clear();
    }
    public int GetPerfectScore()
    {
   
        int perfect=0;
        for (int i = 0; i < l_Nodes.Count; i++)
        {
   
            switch (l_Nodes[i].e_Type)
            {
   
                case enum_BeatType.Single:
                    perfect+=1;
                    break;
                case enum_BeatType.Double:
                    perfect+=2;
                    break;
                case enum_BeatType.Triple:
                    perfect += 3;
                    break;
            }
        }
        return perfect;
    }
    public List<Node> GetNodes()
    {
   
        return l_Nodes;
    }
    public void ForceSort()
    {
   
        l_Nodes.Sort((left,right)=> {
    return left.i_BeatPos >= right.i_BeatPos ? 1 : -1; });
    }
    public bool ContainsNode(int beatPos,bool isLeft)
    {
   
        tempNode = l_Nodes.Find(p => p.i_BeatPos == beatPos);
        return tempNode != null&& tempNode.b_IsLeft==isLeft;
    }
    public void SetNode(int beatPos,bool isLeft,enum_BeatType type)
    {
   
        tempNode = GetNodeByPos(beatPos);
        if (tempNode != null)
        {
   
            int index = l_Nodes.IndexOf(tempNode);
            Node n = new Node(beatPos, isLeft,type);
            l_Nodes[index] = n;
        }
        else
        {
   
            for (int i = 0; i < l_Nodes.Count; i++)
            {
   
                if (l_Nodes[i].i_BeatPos>beatPos)
                {
   
                    l_Nodes.Insert(i,new Node(beatPos,isLeft,type));
                    return;
                }
            }
            l_Nodes.Add(new Node(beatPos, isLeft,type));
        }
    }
    public void AdjustNode(int beatPos, enum_BeatType type)
    {
   

        tempNode = GetNodeByPos(beatPos);
        if (tempNode != null)
        {
   
            int index = l_Nodes.IndexOf(tempNode);
            Node n = new Node(beatPos, tempNode.b_IsLeft, type);
            l_Nodes[index] = n;
        }
        else
        {
   
            Debug.LogError("Can't Adjust A Unexisted Node!Pos:"+beatPos) ;
        }
    }
    public void RemoveNode(int beatPos)
    {
   
        tempNode = GetNodeByPos(beatPos);
        if (tempNode == null)
        {
   
            Debug.LogWarning(beatPos.ToString() + " Node Not Found Howdf U Remove A UnAddNode In Editor?");
            return;
        }
        l_Nodes.Remove(tempNode);
    }
    public Node GetNodeByPos(int beatPos)
    {
   
        return l_Nodes.Find(p => p.i_BeatPos == beatPos);
    }
    public Node GetNodeByIndex(int index)
    {
   
        return l_Nodes[index];
    }
    public int GetNodeIndex(int beatPos)
    {
   
        return l_Nodes.FindIndex(p=>p.i_BeatPos==beatPos);
    }
    public Dictionary<float,int> GetTotalBeatsCenterWithOffset(float f_beatEach)
    {
   
        Dictionary<float, int> dic = new Dictionary<float, int>();
        for (int i = 0; i < l_Nodes.Count; i++)
        {
   
            switch (l_Nodes[i].e_Type)
            {
   
                case enum_BeatType.Single:
                    {
   
                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach,i);
                    }
                    break;
                case enum_BeatType.Double:
                    {
   

                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach, i);
                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach + f_beatEach * .5f, i);
                    }
                    break;
                case enum_BeatType.Triple:
                    {
   
                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach, i);
                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach + f_beatEach / 3,i);
                        dic.Add(F_BeatStartOffset + l_Nodes[i].i_BeatPos * f_beatEach + f_beatEach * 2 / 3,i);
                    }
                    break;
            }
        }
        return dic;
    }
    public List<float> BeatsCenterWithOffset(int beatPos, enum_BeatType type,float f_beatEach)
    {
   
        List<float> beatMids = new List<float>();
        switch (type)
        
  • 8
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值