unity 5.3.4f1 破解5.3.4如何添加IDLE

24978人阅读
Unity3D开发(8)
Unity3D 4.x 版本之后提供了一种新的动画机制Mecanim,虽然目前还支持之前的Animation,但看到Unity3D 4.3&预览版里Sprite的动画也是基于Animator的,可知Mecanim将会是以后动画播放的趋势!
Mecanim是一种基于状态机的结构,不同的状态表示一个动作(或者多个动作融合,或者一个子状态机),状态之间使用一种称为Transition的组件关联,Transition中可以设定一些条件,当在Transition“源状态”下,满足其条件之后,将自动跳转到Transition“目的状态”!(具体用法请参照Unity3D手册)
在一些ARPG的游戏中,比如 端游的DNF和闯关类的街机游戏,连续按攻击键都会触发一套组合攻击,在这个过程中敌人一般是硬直状态的,而且组合攻击的最后一下一般是重击,这样做既增加了连击带来的爽快感,又促使玩家在攻击时采取一定的攻击策略!
Mecanim的状态机设计就很方便的使开发者实现了这一效果!
<span style="font-size:14 color:#.动画状态机
这里只用4个动作描述,待命状态(Idle),攻击1状态(AtkSlice),攻击2状态(AtkStab),攻击3状态(AtkCleave),3个攻击状态分别表示连续按下“普通攻击键”时触发的状态,是有先后顺序关系的,即如果在待命状态下按下“攻击键”,则进入攻击1状态;如果在攻击1状态下继续按下“攻击键”,则进入攻击2状态,如果不按下“攻击键”,则回到待命状态;攻击2状态到攻击3状态同理;攻击3状态认为是重击,即连击结束,回到待命状态!
状态图如下
我希望能以最少的代码和设置完成这个功能,所以只添加了一个状态机参数 ActionCMD,假设 ActionCMD = 1 为进入攻击状态参数
(1)在任意3种攻击状态下,不再继续按下“攻击键”,则回到待命状态,即上图 3条白色Transition
(2)在待命,攻击1,攻击2状态下,继续按下“攻击键”,则跳转到连击状态(即下一个攻击状态),即上图3条蓝色Transition
<span style="font-size:14 color:#.代码实现
(1)状态机设置完成之后,要做的就是在代码中完成对当前状态的判断,以及对状态参数的设置,用以完成动画状态机的切换!
// 使用字符串变量保存当前状态,避免多处引用写错
private static readonly string IdleState = &BaseLayer.Idle&;
private static readonly string AtkSliceState = &BaseLayer.AtkSlice&;
private static readonly string AtkStabState = &BaseLayer.AtkStab&;
private static readonly string AtkCleave = &BaseLayer.AtkCleave&;
// 动画状态机参数Key
private static readonly string ActionCMD = &ActionCMD&;
private Animator animator =
// 当前连击数(即 玩家按下攻击键的次数)
private int curComboCount = 0;
(2)在Start() 中获取Animator组件引用
(3)在Update() 中根据当前状态和输入参数促使状态切换
void Update()
AnimatorStateInfo stateInfo = this.animator.GetCurrentAnimatorStateInfo(0);
if (!stateInfo.IsName(IdleState))
// 每次设置完参数之后,都应该在下一帧开始时将参数设置清空,避免连续切换
this.animator.SetInteger(ActionCMD, 0);
if (stateInfo.IsName(AtkSliceState) && (stateInfo.normalizedTime & 0.6f) && (this.curComboCount == 2))
// 当在攻击1状态下,并且当前状态运行了0.6正交化时间(即动作时长的60%),并且用户在攻击1状态下又按下了“攻击键”
this.animator.SetInteger(ActionCMD, 1);
if (stateInfo.IsName(AtkStabState) && (stateInfo.normalizedTime & 0.8f) && (this.curComboCount == 3))
// 挡在攻击2状态下(同理攻击1状态)
this.animator.SetInteger(ActionCMD, 1);
if (Input.GetKeyUp(KeyCode.J))
// 监听用户输入(假设J键为攻击键)
void Attack()
AnimatorStateInfo stateInfo = this.animator.GetCurrentAnimatorStateInfo(0);
if (stateInfo.IsName(IdleState))
// 在待命状态下,按下攻击键,进入攻击1状态,并记录连击数为1
this.animator.SetInteger(ActionCMD, 1);
this.curComboCount = 1;
else if (stateInfo.IsName(AtkSliceState))
// 在攻击1状态下,按下攻击键,记录连击数为2(切换状态在Update()中)
this.curComboCount = 2;
else if (stateInfo.IsName(AtkStabState))
// 在攻击2状态下,按下攻击键,记录连击数为3(切换状态在Update()中)
this.curComboCount = 3;
这里需要注意的是,在Update() 中使用的0.6和0.8 应该分别小于当前状态跳转到待命状态的参数Exit Time(这个时间也是正交化的)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:762466次
积分:7557
积分:7557
排名:第1687名
原创:70篇
转载:23篇
评论:397条
阅读:176440
文章:27篇
阅读:339813
(3)(1)(3)(2)(1)(5)(1)(2)(4)(3)(2)(5)(4)(4)(5)(5)(6)(4)(2)(8)(2)(1)(2)(3)(6)(2)(1)(1)(2)(3)Unity_unity教程_案例教程_入门教程/自学教程_翼狐网
当前位置:&& &&
Unity软件基础学习教程
下列教程同样可以助您完成;
Unity进阶与案例教程
最新Unity教程
Unity图文教程
行业教程引导:
软件教程引导:Unity3D可以说是高度的Component-Based Architecture,同时它的库提供了大量的全局变量。这些都和我曾接触到的cocos2d-x,和非游戏框架有很大出入,请问各位前辈有没有什么好的方法、模式、框架来组织代码呢?谢谢!
作者:凉鞋链接:来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。为了重构手头的一款项目,翻出来当时未接触Unity候收藏的视频,对于我这种初学者来说全是干货。简单的总结了一下,以后慢慢提炼。关于Unity的架构有如下几种常用的方式。1.EmptyGO在Hierarchy上创建一个空的GameObject,然后挂上所有与GameObject无关的逻辑控制的脚本。使用GameObject.Find()访问对象数据。缺点:逻辑代码散落在各处,不适合大型项目。2.Simple GameManager所有与GameObject无关的逻辑都放在一个单例中。缺点:单一文件过于庞大。3.Manager Of Managers。将不同的功能单独管理。如下:MainManager: 作为入口管理器。 EventManager: 消息管理。 GUIManager: 图形视图管理。 AudioManager: 音效管理。 *PoolManager: go管理(减少动态开辟内存消耗,减少GC)。实现一个简单的PoolManager// 存储动可服用的GameObject。
private List&GameObject& dormantObjects = new List&GameObject&();
// 在dormantObjects获取与go类型相同的GameObject,如果没有则new一个。
public GameObject Spawn(GameObject go)
GameObject temp =
if (dormantObjects.Count & 0)
foreach (GameObject dob in dormantObjects)
if (dob.name == go.name)
// Find an available GameObject
dormantObjects.Remove(temp);
// Now Instantiate a new GameObject.
temp = GameObject.Instantialte(go) as GameO
temp.name = go.
// 将用完的GameObject放入dormantObjects中
public void Despawn(GameObject go)
go.transform.parent = PoolManager.
go.SetActive(false);
dormantObject.Add(go);
//FIFO 如果dormantObjects大于最大个数则将之前的GameObject都推出来。
public void Trim()
while (dormantObjects.Count & Capacity)
GameObject dob = dormantObjects[0];
dormantObjects.RemoveAt(0);
Destroy(dob);
缺点:(1)不能管理prefabs。(2)没有进行分类。更好的实现方式是将一个PoolManager分成:若干个 SpawnPool。每个SpawnPool分成PrefabPool和PoolManager。PrefabPool负责Prefab的加载和卸载。PoolManager与之前的PoolMananger功能一样,负责GameObject的Spawn、Despawn和Trim。要注意的是:(1)每个SpawnPool是EmeptyGO。(2)每个PoolManager管理两个List (Active,Deactive)。讲了一堆,最后告诉有一个NB的插件叫Pool Manager- -。*LevelManager: 关卡管理。推荐插件:MadLevelManager。GameManager: 游戏管理。*SaveManager: 配置管理。实现Resume,功能玩到一半数据临时存储。推荐SaveManager插件。可以Load、Save均采用二进制(快!!!)所有C#类型都可以做Serialize。数据混淆,截屏操作。MenuManager 菜单管理。4.将View和Model之间增加一个媒介层。MVCS:StrangeIOC插件。MVVM:uFrame插件。《Game Design Workshop》读书笔记:之前看的英文第三版,看了好几天的东西,今天下了个中文第一版,看一会全跟上来了,还是先看中文版的吧。(1)设计师要通过玩家的眼睛看世界,不要陷在画面、故事情节、游戏的特性中。整体性才是设计师要关注的。玩家玩你的游戏玩得过瘾才能玩很长时间。(2)游戏的测试很重要,超级重要!,要从头测到尾,多倾听,适当妥协保证团队有效沟通,美术考虑美术,程序考虑程序,制作人考虑预算,设计师负责提高游戏体验。练习1.1 成为一名测试者。写得有点仓促,最后附上我晚上画的小作品------------------------------------------------------------------------------------------------------写得不是很全,还是建议看一下视频------------------------------------------------------------------------------------------------------------------------------------日更新-------------------------------------------------Unity 游戏框架搭建 (二) 单例的模板上一篇文章中说到的manager of managers,其中每个manager都是单例的实现,当然也可以使用静态类实现,但是相比于静态类的实现,单例的实现更为通用,可以适用大多数情况。如何设计这个单例的模板?先分析下需求,当设计一个manager时候,我们希望整个程序只有一个该manager对象实例,一般马上能想到的实现是这样的:public class XXXManager {
private static XXXManager instance =
private XXXManager {
// to do ...
public static XXXManager() {
if (instance == null)
instance = new XXXManager();
如果一个游戏需要10个各种各样的manager,那么以上这些代码要复制粘贴好多遍。重复的代码太多!!!想要把重复的代码抽离出来,怎么办?答案是引入泛型。实现如下:using S
using System.Collections.G
using System.T
using System.R
namespace QFramework {
public abstract class QSingleton&T& where T : QSingleton&T&
protected static T instance =
protected QSingleton()
public static T Instance()
if (instance == null)
// 如何new 一个T???
为了可以被继承,静态实例和构造方法都使用protect修饰符。以上的问题很显而易见,那就是不能new一个泛型(3月9日补充:并不是不能new一个泛型,参考:),(4月5日补充:有同学说可以new一个泛型的实例,并且编译通过了,好吧,这里不用new的原因是,无法显示调用private的构造函数)。因为泛型本身不是一个类型,那该怎么办呢?答案是使用反射。实现如下:using S
using System.Collections.G
using System.T
using System.R
/// &summary&
/// 1.泛型
/// 2.反射
/// 3.抽象类
/// 4.命名空间
/// &/summary&
namespace QFramework {
public abstract class QSingleton&T& where T : QSingleton&T&
protected static T instance =
protected QSingleton()
public static T Instance()
if (instance == null)
// 先获取所有非public的构造方法
ConstructorInfo[] ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
// 从ctors中获取无参的构造方法
ConstructorInfo ctor = Array.Find(ctors, c =& c.GetParameters().Length == 0);
if (ctor == null)
throw new Exception("Non-public ctor() not found!");
// 调用构造方法
instance = ctor.Invoke(null) as T;
以上就是最终实现了。这个实现是在任何C#程序中都是通用的。其测试用例如下所示:using QF
// 1.需要继承QSingleton。
// 2.需要实现非public的构造方法。
public class XXXManager : QSingleton&XXXManager& {
private XXXManager() {
// to do ...
public static void main(string[] args)
XXXManager.Instance().xxxyyyzzz();
总结这个单例的模板是平时用得比较顺手的工具了,其实现是在其他的框架中发现的,拿来直接用了。反射的部分可能会耗一些性能,但是只会执行一次。在Unity中可能会需要继承MonoBehaviour的单例,因为很多游戏可能会只创建一个GameObject,用来获取MonoBehaviour的生命周期,这些内容会再下一讲中介绍:)。 附:和:转载请注明地址:微信公众号:QWERTeam------------------------------------------------------------------------------------------------------------------------------------日更新-------------------------------------------------上一篇文章讲述了如何设计C#单例的模板。也随之抛出了问题:如何设计接收MonoBehaviour生命周期的单例的模板?如何设计?先分析下需求:1.约束脚本实例对象的个数。 2.约束GameObject的个数。 3.接收MonoBehaviour生命周期。 4.销毁单例和对应的GameObject。首先,第一点,约束脚本实例对象的个数,这个在上一篇中已经实现了。但是第二点,约束GameObject的个数,这个需求,还没有思路,只好在游戏运行时判断有多少个GameObject已经挂上了该脚本,然后如果个数大于1抛出错误即可。 第三点,通过继承MonoBehaviour实现,只要覆写相应的回调方法即可。 第四点,在脚本销毁时,把静态实例置空。 完整的代码就如下所示:using UnityE
/// &summary&
/// 需要使用Unity生命周期的单例模式
/// &/summary&
namespace QFramework {
public abstract class QMonoSingleton&T& : MonoBehaviour where T : QMonoSingleton&T&
protected static T instance =
public static T Instance()
if (instance == null)
instance = FindObjectOfType&T&();
if (FindObjectsOfType&T&().Length & 1)
QPrint.FrameworkError ("More than 1!");
if (instance == null)
string instanceName = typeof(T).N
QPrint.FrameworkLog ("Instance Name: " + instanceName);
GameObject instanceGO = GameObject.Find(instanceName);
if (instanceGO == null)
instanceGO = new GameObject(instanceName);
instance = instanceGO.AddComponent&T&();
DontDestroyOnLoad(instanceGO);
//保证实例不会被释放
QPrint.FrameworkLog ("Add New Singleton " + instance.name + " in Game!");
QPrint.FrameworkLog ("Already exist: " + instance.name);
protected virtual void OnDestroy()
instance =
总结:目前已经实现了两种单例的模板,一种是需要接收Unity的生命周期的,一种是不需要接收生命周期的,可以配合着使用。虽然不是本人实现的,但是用起来可是超级爽快,2333。附上参考的 和转载请注明地址:微信公众号:QWERTeam------------------------------日更新-------------------------------------------------为什么用有限状态机?
之前做过一款跑酷游戏,跑酷角色有很多状态:跑、跳、二段跳、死亡等等。一开始是使用if/switch来切换状态,但是每次角色添加一个状态(提前没规划好),所有状态处理相关的代码就会指数级增长,那样就会嗅出代码的坏味道了。在这种处理状态并且状态数量不是特别多的情况下,自然就想到了引入状态机。优点:
1.使代码整洁,状态容易扩展和管理。
2.可复用。
3.还没想到.....缺点:
1.也没想到......什么是有限状态机?
解释不清楚,看了下百度百科。反正是一种数据结构,一个解决问题的工具。
从百度百科可以看到,有限状态机最最最基础的概念有两个:状态和转移。
从刚才跑酷的例子来讲,跑、跳、二段跳等这些就是角色的状态。如图所示:
主角从跑状态切换到跳状态,从跳状态切换到二段跳状态,这里的切换就是指状态的转移。状态的转移是有条件的,比如主角从跑状态不可以直接切换到二段跳状态。但是可以从二段跳状态切换到跑状态。
另外,一个基本的状态有:进入状态、退出状态、接收输入、转移状态等动作。但是仅仅作为跑酷的角色的状态管理来说,只需要转移状态就足够了。有兴趣的同学可以自行扩展。如何实现?
恰好之前看到过一个还算简易的实现(简易就是指我能看得懂- -,希望大家也是),原版是用lua实现的,我的跑酷游戏是用C#实现的,所以直接贴出C#代码。using UnityE
using System.C
using System.Collections.G
public class FSM {
// 定义函数指针类型
public delegate void FSMTranslationCallfunc();
/// &summary&
/// 状态类
/// &/summary&
public class FSMState
public FSMState(string name)
this.name =
/// &summary&
/// 存储事件对应的条转
/// &/summary&
public Dictionary &string,FSMTranslation& TranslationDict = new Dictionary&string,FSMTranslation&();
/// &summary&
/// 跳转类
/// &/summary&
public class FSMTranslation
public FSMState fromS
public FSMState toS
public FSMTranslationC // 回调函数
public FSMTranslation(FSMState fromState,string name, FSMState toState,FSMTranslationCallfunc callfunc)
this.fromState = fromS
this.toState
this.name =
this.callfunc =
// 当前状态
private FSMState mCurS
Dictionary &string,FSMState& StateDict = new Dictionary&string,FSMState&();
/// &summary&
/// 添加状态
/// &/summary&
/// &param name="state"&State.&/param&
public void AddState(FSMState state)
StateDict [state.name] =
/// &summary&
/// 添加条转
/// &/summary&
/// &param name="translation"&Translation.&/param&
public void AddTranslation(FSMTranslation translation)
StateDict [translation.fromState.name].TranslationDict [translation.name] =
/// &summary&
/// 启动状态机
/// &/summary&
/// &param name="state"&State.&/param&
public void Start(FSMState state)
mCurState =
/// &summary&
/// 处理事件
/// &/summary&
/// &param name="name"&Name.&/param&
public void HandleEvent(string name)
if (mCurState != null && mCurState.TranslationDict.ContainsKey(name)) {
Debug.LogWarning ("fromState:" + mCurState.name);
mCurState.TranslationDict [name].callfunc ();
mCurState = mCurState.TranslationDict [name].toS
Debug.LogWarning ("toState:" + mCurState.name);
测试代码(需自行修改)://
DoubleJump,
// 创建状态
FSM.FSMState idleState = new FSM.FSMState("idle");
FSM.FSMState runState
= new FSM.FSMState("run");
FSM.FSMState jumpState = new FSM.FSMState("jump");
FSM.FSMState doubleJumpState = new FSM.FSMState("double_jump");
FSM.FSMState dieState
= new FSM.FSMState("die");
// 创建跳转
FSM.FSMTranslation touchTranslation1 = new FSM.FSMTranslation(runState,"touch_down",jumpState,Jump);
FSM.FSMTranslation touchTranslation2 = new FSM.FSMTranslation(jumpState,"touch_down",doubleJumpState,DoubleJump);
FSM.FSMTranslation landTranslation1 = new FSM.FSMTranslation(jumpState,"land",runState,Run);
FSM.FSMTranslation landTranslation2 = new FSM.FSMTranslation(doubleJumpState,"land",runState,Run);
// 添加状态
PlayerModel.Instance ().fsm.AddState (idleState);
PlayerModel.Instance ().fsm.AddState (runState);
PlayerModel.Instance ().fsm.AddState (jumpState);
PlayerModel.Instance ().fsm.AddState (doubleJumpState);
PlayerModel.Instance ().fsm.AddState (dieState);
// 添加跳转
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (touchTranslation2);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation1);
PlayerModel.Instance ().fsm.AddTranslation (landTranslation2);
PlayerModel.Instance ().fsm.Start (runState);
贴上代码地址址:
就这些,想要进一步扩展的话,可以给FSMState类添加EnterCallback和ExitCallback等委托,然后在FSM的HandleEvent方法中进行调用。当时对跑酷的项目来说够用了,接没继续扩展了,我好懒- -,懒的借口是:没有最好的设计,只有最适合的设计,233333。附:转载请注明地址:微信公众号:QWERTeam
准确地说,代码作为Unity项目里的一种资源,此问题应该扩展到如何组织Unity资源。简单说说我们的经验:&br&- Unity有一些自身的约定,譬如项目里的Editor,Plugins等目录作为编辑器,插件目录等等。知名的插件会自己存放一个目录,譬如NGUI等。&br&
所以我们自己的代码,一般目录名会以下划线开头,譬如 &_Scripts&, &_Prefabs&等。&br&
对于场景,文档等目录,用两条下划线,以便他们能排在最顶部。&br&- 代码用C#,别用JS。必要的话用namespace将自己的代码括起来。我们是用namespace把自己积攒的公用库包住。&br&- C#的注释要认真写,打///就能帮你补全了,没理由偷懒。&br&- 每个程序文件开头要用一段注释写修改Log,谁改过什么简单留一条说明。就算用了Unity的版本管理或者Git,那些log终究会丢失,只有认真把log写在代码里,才会有意识去认真优化它。&br&- Unity的脚本逻辑,就功能而言大体分为两种,一种是比较独立的,譬如爆炸之后1秒钟消失,这种单独写个脚本绑定到目标上即可。&br&
更多的是脚本里与其它的脚本进行交互。Unity里提供了一种万金油的方法是SendMessage, 这种方法性能略差,如果你调用的频率不高,随便用也无妨。另一种方法是直接通过对象的实例去调用。&br&&br&
我们的做法是写几个公用的控制器,让它们各司其职,负责各自的事情:&br&- 写一个一个GlobalManager.cs来控制游戏的全局变量及全局方法。静态类模式。譬如当前玩到第几大关第几小关,玩家的金币数量等。&br&- 写一个GameController.cs来控制当前关的游戏进程。单实例模式。游戏的主循环也是用它控制。初始化,胜利、失败判定等等。&br&- 写一个InputController.cs来控制所有的用户输入。单实例模式。鼠标、键盘、触摸屏,我们做游戏是保证同时支持这三种输入的,因为大部分时间是在PC上测试。&br&
关于GameController与InputController的联系,有点让人纠结。一般来讲是在InputContoller里调用GameController.Instance.Foo()执行方法。或者直接对Input写成Listener的模式,让GameController去监听。&br&- 其它的类似菜单控制器,声音控制器,成就控制器,IAP虚拟道具控制器等等,也是采用类似的方法管理。&br&- 关于PlayerPref的操作,统一写成静态类的get/set模式,程序中哪里要用则直接读写。&br&- 如果你的项目里场景的数量少(&5),那么拖入场景的资源可以很随意。如果场景数量很多(几十个,有的解谜游戏每个关卡就是一个场景),那么拖入场景的prefab数量一定要少。&br&- 设计你的prefab资源里,你要想像当其他人拿到这些资源,是否直接拖入一个空场景里就能run,顶多再简单设置几下。如果你设计的资源不能做到这些,那么得好好重新想想。&br&&br&&br&写了这些,感觉写不下去了。&br&想吃透Unity,起码得真做出几款产品放上线才行。真正做产品的过程中会碰到各种各样意想不到的问题,代码不断地被重构和妥协,不存在什么最佳的方案。&br&暂时就写这些吧,希望能抛砖引玉。
准确地说,代码作为Unity项目里的一种资源,此问题应该扩展到如何组织Unity资源。简单说说我们的经验:- Unity有一些自身的约定,譬如项目里的Editor,Plugins等目录作为编辑器,插件目录等等。知名的插件会自己存放一个目录,譬如NGUI等。 所以我们自己的代…
我的代码大概遵循这么几条原则吧(想到哪儿写哪儿不分先后):&br&1.逻辑脚本基于场景划分&br&2.抽离静态配置数据、全局管理数据以及对局临时数据的管理&br&3.使用单例模式创建不依赖于场景的游戏对象及其上的全局管理器&br&4.避免使用GameObject.Find以及SendMessage,声明对象引用以显示标明脚本之间的依赖性,活用delegate解耦合&br&5.多用组合少用继承(Component的架构真的是太棒了)&br&6.数据行为与逻辑表现分离,即V与MC的分离,换句话说多写class少写MonoBehavior。(通常初期在快速开发原型时会把一个功能全部实现写在一个继承于MonoBehavior的脚本中,尽早进行重构,抽离出负责数据管理与控制的类,这对于后期功能的增加与修改时很有必要的)&br&7.善用Coroutine(Coroutine真是太方便了)&br&8.尽量能够使用自定义的配置文件辅助Prefab上脚本参数的配置。&br&&br&总的来说记得知乎上看到谁说过cocos2d是程序员友好的,而Unity3D是设计师友好的,写了这么多年Unity3D代码我真是觉得我的思考方式越来越像策划而不是程序员了,使用Unity3D开发,写代码应该只占了大概50%的工作,另外50%都在编辑器上,如果你用过相信你懂得。
我的代码大概遵循这么几条原则吧(想到哪儿写哪儿不分先后):1.逻辑脚本基于场景划分2.抽离静态配置数据、全局管理数据以及对局临时数据的管理3.使用单例模式创建不依赖于场景的游戏对象及其上的全局管理器4.避免使用GameObject.Find以及SendMessage,声明…
已有帐号?
无法登录?
社交帐号登录
Funk音乐/Poppin爱好者/独立开发者/}

我要回帖

更多关于 unity5.3 添加按钮 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信