• 方法①:使用变量在Update中计时 方法②:使用协程Coroutine 方法③:使用InvokeRepeating

    最近开始学习Unity,也想开始学习写一些简单的博客。
    在网上学习了一些关于定时器的写法,在此简单总结一下,方便自己以后用到时查阅。

    需求:制作定时器,运行3秒后执行第一次,之后每隔3秒执行一次操作。

    方法①:使用变量在Update中计时,也是各种书中最常见的方法。

    public class TestTimer : MonoBehaviour {
    
        private float lastTime;
        private float curTime;
    
        void Start () {
            lastTime = Time.time;
        }
    
        void Update () {
            curTime = Time.time;
            if (curTime - lastTime >= 3)
            {
                Debug.Log("work");
                lastTime = curTime;
               }
        }
    }

    方法②:使用协程Coroutine

    public class TestTimer : MonoBehaviour {
    
        void Start () {
            StartCoroutine(Do()); // 开启协程
        }
    
        IEnumerator Do()
        {
            while (true) // 还需另外设置跳出循环的条件
            {
                yield return new WaitForSeconds(3.0f);
                Debug.Log("work");
            }
        }
    }

    方法③:使用InvokeRepeating

    public class TestTimer : MonoBehaviour {
    
        void Start () {
            InvokeRepeating("Do", 3.0f, 3.0f);
        }
    
        void Do()
        {
            Debug.Log("work");
        }
    }

    个人认为,使用InvokeRepeating调用写法最简洁。

    展开全文
  • Unity3D教程:物体跟随鼠标点击处移动 Unity3D音乐开关与音量条 Unity3D教程:GUILayout.Window和GUI.Window的区别 在游戏中改变地形高度 教你如何创建unity3d多个定时器,以及定时器的其他操作 Unity3D使用LitJson...
  • using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /// &.../// 描述:处理计时功能,包括计时开始,暂停计时 ...public class CalculatorTime : ...
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    /// <summary>
    ///  描述:处理计时功能,包括计时开始,暂停计时
    /// </summary>
    public class CalculatorTime : MonoBehaviour {
    
        public float time_All = 300;//计时的总时间(单位秒)  
        public float time_Left;//剩余时间  
        public bool isPauseTime = false;
        public Text time;
        // Use this for initialization  
        void Start()
        {
            time_Left = time_All;
        }
    
        // Update is called once per frame  
        void Update()
        {
            if (!isPauseTime)
            {
                if (time_Left > 0)
                    StartTimer();
            }
    
        }
        /// <summary>  
            /// 开始计时   
            /// </summary>  
        void StartTimer()
        {
            time_Left -= Time.deltaTime;
            time.text = GetTime(time_Left);
    
        }
        /// <summary>  
            ///继续游戏,这个暂时加在这里,后期代码重构时加在UIControl中   
            /// </summary>  
        public void ContinueGame()
        {
    
            isPauseTime = false;
            //Time.timeScale = 1;
        }
    
        /// <summary>  
            /// 暂停计时  
            /// </summary>  
        public void PauseTimer()
        {
            isPauseTime = true;
            //Time.timeScale = 0;
        }
        /// <summary>  
            /// 获取总的时间字符串  
            /// </summary>  
        string GetTime(float time)
        {
            return GetHour(time)+ GetMinute(time) + GetSecond(time);
    
        }
    
        /// <summary>  
            /// 获取小时  
            /// </summary>  
        string GetHour(float time)
        {
            int timer = (int)(time / 3600);
            string timerStr;
            if (timer < 10)
                timerStr = "0" + timer.ToString() + ":";
            else
                timerStr = timer.ToString() + ":";
            return timerStr;
        }
        /// <summary>  
            ///获取分钟   
            /// </summary>  
        string GetMinute(float time)
        {
            int timer = (int)((time % 3600) / 60);
            string timerStr;
            if (timer < 10)
                timerStr = "0" + timer.ToString() + ":";
            else
                timerStr = timer.ToString() + ":";
            return timerStr;
        }
        /// <summary>  
            /// 获取秒  
            /// </summary>  
        string GetSecond(float time)
        {
            int timer = (int)((time % 3600) % 60);
            string timerStr;
            if (timer < 10)
                timerStr = "0" + timer.ToString();
            else
                timerStr = timer.ToString();
    
            return timerStr;
        } 
    
    }
    

     

    展开全文
  • 教你如何创建unity3d多个定时器,以及定时器的其他操作 Unity3D使用LitJson解析服务器上的JSON IOS下文件保存和读取 Unity3D教程:游戏开发算法(二) ClickShowWindow 点击显示窗口 鼠标选中物体并拖动物体的方法 ...
  • 掌握Unity3D基本元素 1.1 简单的游戏 1.1.1在场景中创建一个立方体 1.1.2编写可以使立方体运动的程序 1.1.3测试游戏1.1.4总结1.2 资源导入1.3 山体系统1.4 灯光1.5 材质1.6 预设1.6.1制作预设1.6.2例子1.7 声音...

    Unity 3D网络游戏实战(全)

    目录:

      掌握Unity3D基本元素

    1.1 简单的游戏

    1.1.1在场景中创建一个立方体

    1.1.2编写可以使立方体运动的程序

    1.1.3测试游戏1.1.4总结1.2 资源导入1.3 山体系统1.4 灯光1.5 材质1.6 预设1.6.1制作预设1.6.2例子1.7 声音1.7.1音源1.7.2接收器1.7.3简单播放器1.8 GUI1.8.1用Unity3D编写HelloWorld程序1.8.2用Unity3D编写登录框1.9导出游戏第

    2 章 驾驶梦想中的坦克备用章节名:坦克控制单元

    2.1行走控制2.1.1Unity3D的脚本2.1.2获取输入操作2.1.3坐标变换2.2 相机跟随2.2.1跟随的数学原理2.2.2相机跟随的功能实现2.2.3鼠标控制相机的角度2.2.4滚轮调整相机与坦克的距离2.3 坦克的物理特征2.3.1 Unity3D的物理引擎2.3.2 车轮碰撞器2.4 驾驶坦克2.5转动的轮子2.6滚动的履带

    第3章 开炮击毁敌人备用章节名:火炮系统

    3.1 旋转的炮塔3.2 俯仰的炮管 3.3 开炮!3.3.1制作3.3.2制作爆炸效果3.3.3逻辑3.3.4开火3.4 坦克的控制类型3.5 摧毁敌军3.6 准心3.7 生命指示条3.8 击中提示3.8.1谁发射了3.8.2被谁击中3.8.3击杀提示3.9 的音效3.9.1射击音效3.9.2爆炸声音

    第4章 进击的人工智能备用章节名:人工智能

    4.1 实现人工智能的一般方法4.1.1有限状态机4.1.2分层有限状态机4.2 搜寻目标4.2.1编写AI类4.2.2在Tank中调用AI类4.3 向敌人开炮4.3.1旋转的炮塔4.3.2俯仰的炮管 4.3.3开炮!4.4 路点和路径4.4.1路点4.4.2路径4.4.3一种生成路径的方法4.4.4给坦克指定路径4.5 走向目的地4.6 使用NavMesh计算路径4.6.1NavMesh的原理4.6.2生成导航图4.6.3生成路径4.7 行为决策4.8 战场4.8.1战场逻辑4.8.2两军对峙

    第5章 代码分离的界面系统

    5.1 了解Unity UI系统(UGUI)5.1.1 Canvas画布5.1.2 EventSystem5.1.3 RectTransform5.1.4 其他UGUI组件5.1.5 事件触发5.2 制作坦克游戏界面5.3 界面基类PanelBase5.3.1 代码与资源分离的优势5.3.2 界面系统的设计5.3.3 PanelBase的设计5.4 界面管理器PanelMgr5.4.1 层级管理5.4.2 打开面板OpenPanel5.4.3 关闭面板ClosePanel5.5 TitlePanel和InfoPanel5.6 调用界面系统5.7 胜负面板5.8 设置面板

    第6章 网络基础

    6.1 七层网络模型6.1.1 应用层6.1.2传输层6.1.3网络层6.1.4数据链路层6.2 IP与端口6.3 TCP协议6.3.1 TCP连接的建立6.3.2 TCP的数据传输6.3.3 TCP连接的终止6.4 Socket套接字6.5 同步Socket程序6.5.1 新建控制台程序6.5.2编写服务端代码6.5.3编写客户端程序6.6 异步socket程序6.6.1 连接类(state)6.6.2 异步服务端6.6.3 开启服务端6.6.4 编写客户端程序6.7 Mysql6.7.1 Mysql基础入门6.7.2 留言板服务端程序6.7.3 调试程序6.8 类的序列化6.9 定时器6.10 线程互斥6.11 通讯协议和消息列表6.11.1 通讯协议6.11.2 服务端程序6.11.3 客户端场景6.11.4 消息列表6.11.5 客户端程序6.11.6 调试

    第7章 游戏服务端框架

    7.1服务端架构7.1.1 总体架构7.1.2游戏流程7.1.3连接数据结构7.1.4数据库结构7.1.5项目结构7.2 数据管理类DataMgr7.2.1 数据库结构7.2.2 玩家数据7.2.3 Player的初步版本7.2.4 连接数据库7.2.5 防止sql注入7.2.6 Register注册7.2.7 CreatePlayer创建角色7.2.8 登录校验7.2.9 获取角色数据7.2.10 保存角色数据7.2.11 调试7.3 处理粘包分包7.3.1 Conn类7.3.2 ServNet网络处理类7.3.3 ReceiveCb的沾包分包处理7.3.4 发送消息7.3.5 启动服务端7.3.6 客户端程序7.4 心跳7.4.1 时间戳7.4.2使用定时器7.4.3心跳消息7.4.4调试7.5 协议7.5.1协议基类7.5.2字符串协议7.5.3字节流协议7.5.4字节流辅助方法7.5.5使用协议7.5.6客户端程序7.5.7调试7.6 中间层player类7.7 消息分发7.7.1消息分发类7.7.2 NetServ的消息分发7.8 注册登录7.8.1 协议7.8.2 注册7.8.3 登录7.8.4 登出7.8.5 获取分数7.8.6 增加分数7.8.7 Print输出服务端信息7.8.8 Main7.8.9 客户端7.8.10 调试

    第8章 客户端网络模块

    8.1 网络模块设计8.2 委托8.3 MsgDistribution消息分发8.4 Connection连接8.4.1 连接服务端8.4.2 异步回调8.4.3 添加消息8.4.4 发送数据8.4.5 心跳处理8.5 NetMgr网络管理8.6 登录注册8.6.1登录注册面板8.6.2登录面板功能8.6.3注册面板功能8.7 服务端程序8.7.1协议8.7.2场景8.7.3协议处理8.7.4事件处理8.8 客户端程序8.8.1客户端资源8.8.2客户端程序8.9调试框架

    第9章 房间系统

    9.1 房间协议9.2 服务端房间类9.3 服务端协议处理9.4 客户端界面制作9.5 客户端协议处理0章 同步战场系统10.1 战场协议10.2 服务端战场类10.3 服务端协议处理10.4 客户端界面制作10.5 客户端协议处理10.6

    下载地址:https://pan.baidu.com/s/1kNggFuy1pjLXAb3GrdCJ2w

    关注微信公众号获取提取码:

      输入:3dsz   获取提取码  

    转载于:https://www.cnblogs.com/kuoAT/p/9528471.html

    展开全文
  • 简单打飞碟小游戏 简单打飞碟小游戏 游戏规则与游戏要求 游戏UML类图 游戏实现过程 ...鼠标点击飞碟,即可获得分数,不同飞碟分数不一样,飞碟的初始位置与飞行速度随机,随着分数增加,游戏难度增加。...

    简单打飞碟小游戏


    游戏规则与游戏要求

    • 规则
      鼠标点击飞碟,即可获得分数,不同飞碟分数不一样,飞碟的初始位置与飞行速度随机,随着分数增加,游戏难度增加。初始时每个玩家都有6条生命,漏打飞碟扣除一条生命,直到生命为0游戏结束。

    • 要求:

      • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
      • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
    • 扩展:

      • 用自定义组件定义几种飞碟,编辑并赋予飞碟一些属性,做成预制

    游戏UML类图

    • 很多类都是使用之前的代码,修改过或者新建的类,在UML中表示出来了
      UML类图

    游戏实现过程

    动作部分

    • FlyActionManager

      飞碟飞行的动作管理类,当场景控制器需要飞碟飞行的时候,调用动作管理类的方法,让飞碟飞行

    public class FlyActionManager : SSActionManager
    {
    
        public UFOFlyAction fly;                            //飞碟飞行的动作
        public FirstController scene_controller;             //当前场景的场景控制器
    
        protected void Start()
        {
            scene_controller = (FirstController)SSDirector.GetInstance().CurrentScenceController;
            scene_controller.action_manager = this;     
        }
        //飞碟飞行
        public void UFOFly(GameObject disk, float angle, float power)
        {
            fly = UFOFlyAction.GetSSAction(disk.GetComponent<DiskData>().direction, angle, power);
            this.RunAction(disk, fly, this);
        }
    }
    • UFOFlyAction

      给飞碟一个方向和一个力,然后飞碟模拟做有向下加速度的飞行动作,直到飞碟不在相机范围内,就停止动作。可以设定飞碟位置y方向的值,当它小于多少的时候,不再飞行,然后等待场景控制器和飞碟工厂进行配合回收飞碟。其实可以使用Rigidbody组件,开启Unity的物理模拟效果。

    public class UFOFlyAction : SSAction
    {
        public float gravity = -5;                                 //向下的加速度
        private Vector3 start_vector;                              //初速度向量
        private Vector3 gravity_vector = Vector3.zero;             //加速度的向量,初始时为0
        private float time;                                        //已经过去的时间
        private Vector3 current_angle = Vector3.zero;               //当前时间的欧拉角
    
        private UFOFlyAction() { }
        public static UFOFlyAction GetSSAction(Vector3 direction, float angle, float power)
        {
            //初始化物体将要运动的初速度向量
            UFOFlyAction action = CreateInstance<UFOFlyAction>();
            if (direction.x == -1)
            {
                action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
            }
            else
            {
                action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
            }
            return action;
        }
    
        public override void Update()
        {
            //计算物体的向下的速度,v=at
            time += Time.fixedDeltaTime;
            gravity_vector.y = gravity * time;
    
            //位移模拟
            transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;
            current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;
            transform.eulerAngles = current_angle;
    
            //如果物体y坐标小于-10,动作就做完了
            if (this.transform.position.y < -10)
            {
                this.destroy = true;
                this.callback.SSActionEvent(this);      
            }
        }
    
        public override void Start() { }
    }

    飞碟预制与重用部分

    • 用自定义组件定义几种飞碟

    使用序列化的方法,将DiskData的属性显示在Inspector中。需要我们新建一个脚本,并且继承Editor,在类的前面添加:[CustomEditor(typeof(DiskData))],这里的DiskData就是要实现自定义组件的类。在这之后添加[CanEditMultipleObjects],实现了多个对象可以不同的修改。如果没有这个标签,那么在Inspector修改之后,拥有这个DiskData作为组件的预制体所有修改都会同步。SerializedProperty是我们需要序列化的属性,通过EditorGUILayout的不同的方法,可以在Inspector中用不同方式呈现我们序列化的属性。序列化的属性的呈现方式需要在OnInspectorGUI中进行编写。

    //脚本DiskData
    public class DiskData : MonoBehaviour
    {
        public int score = 1;                               //射击此飞碟得分
        public Color color = Color.white;                   //飞碟颜色
        public Vector3 direction;                           //飞碟初始的位置
        public Vector3 scale = new Vector3( 1 ,0.25f, 1);   //飞碟大小
    }
    
    //脚本MyDiskEditor
    [CustomEditor(typeof(DiskData))]
    [CanEditMultipleObjects]
    public class MyDiskEditor: Editor
    {
        SerializedProperty score;                              //分数
        SerializedProperty color;                              //颜色
        SerializedProperty scale;                              //大小
    
        void OnEnable()
        {
            //序列化对象后获得各个值
            score = serializedObject.FindProperty("score");
            color = serializedObject.FindProperty("color");
            scale = serializedObject.FindProperty("scale");
        }
    
        public override void OnInspectorGUI()
        {
            //更新serializedProperty,始终在OnInspectorGUI的开头执行此操作
            serializedObject.Update();
            //设置滑动条
            EditorGUILayout.IntSlider(score, 0, 5, new GUIContent("score"));
    
            if (!score.hasMultipleDifferentValues)
            {
                //显示进度条
                ProgressBar(score.intValue / 5f, "score");
            }
            //显示值
            EditorGUILayout.PropertyField(color);
            EditorGUILayout.PropertyField(scale);
            //将更改应用于serializedProperty,始终在OnInspectorGUI的末尾执行此操作
            serializedObject.ApplyModifiedProperties();
        }
        private void ProgressBar(float value, string label)
        {
            Rect rect = GUILayoutUtility.GetRect(18, 18, "TextField");
            EditorGUI.ProgressBar(rect, value, label);
            //中间留一个空行
            EditorGUILayout.Space();
        }
    }

    如果自定义组件只是运用在一个预制体上,也可以使用其他方法简化代码,具体操作请看官方文档。我设计了三种飞碟,把DiskData脚本挂载在每个飞碟预制体上就可以对它们进行设置了,最后实现效果如下图。

    自定义组件

    • DiskFactory

    飞碟工厂类,根据回合的不同实现随机发送不同的飞碟。在场景控制器需要某种飞碟的时候,飞碟工厂从仓库(List <DiskData> free)中获取这种飞碟,如果仓库中没有,则新的实例化一个飞碟,然后添加到正在使用的飞碟列表中。当场景控制器发现飞碟被打中或者飞碟掉出摄像机视野外,将执行回收飞碟。(ps: 这里也留了一个问题为啥List中保存DiskData,而不直接保存GameObject,我觉得是DiskData可以用gameObject属性直接得到GameObject对象,而GameObject对象要用GetComponent<DiskData>()方法去查找DiskData组件,而GetComponent<>()比直接获取属性慢的多,使用DiskData频率可能高于直接使用GameObject对象,所以优选保存DiskData)。。。然而获取飞碟的时候我返回了GameObject,而且在场景控制器我也是保存的GameObject :)

    public class DiskFactory : MonoBehaviour
    {
        public GameObject disk_prefab = null;                 //飞碟预制体
        private List<DiskData> used = new List<DiskData>();   //正在被使用的飞碟列表
        private List<DiskData> free = new List<DiskData>();   //空闲的飞碟列表
    
        public GameObject GetDisk(int round)
        {
            int choice = 0;
            int scope1 = 1, scope2 = 4, scope3 = 7;           //随机的范围
            float start_y = -10f;                             //刚实例化时的飞碟的竖直位置
            string tag;
            disk_prefab = null;
    
            //根据回合,随机选择要飞出的飞碟
            if (round == 1)
            {
                choice = Random.Range(0, scope1);
            }
            else if(round == 2)
            {
                choice = Random.Range(0, scope2);
            }
            else
            {
                choice = Random.Range(0, scope3);
            }
            //将要选择的飞碟的tag
            if(choice <= scope1)
            {
                tag = "disk1";
            }
            else if(choice <= scope2 && choice > scope1)
            {
                tag = "disk2";
            }
            else
            {
                tag = "disk3";
            }
            //寻找相同tag的空闲飞碟
            for(int i=0;i<free.Count;i++)
            {
                if(free[i].tag == tag)
                {
                    disk_prefab = free[i].gameObject;
                    free.Remove(free[i]);
                    break;
                }
            }
            //如果空闲列表中没有,则重新实例化飞碟
            if(disk_prefab == null)
            {
                if (tag == "disk1")
                {
                    disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, start_y, 0), Quaternion.identity);
                }
                else if (tag == "disk2")
                {
                    disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, start_y, 0), Quaternion.identity);
                }
                else
                {
                    disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, start_y, 0), Quaternion.identity);
                }
                //给新实例化的飞碟赋予其他属性
                float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
                disk_prefab.GetComponent<Renderer>().material.color = disk_prefab.GetComponent<DiskData>().color;
                disk_prefab.GetComponent<DiskData>().direction = new Vector3(ran_x, start_y, 0);
                disk_prefab.transform.localScale = disk_prefab.GetComponent<DiskData>().scale;
            }
            //添加到使用列表中
            used.Add(disk_prefab.GetComponent<DiskData>());
            return disk_prefab;
        }
    
        //回收飞碟
        public void FreeDisk(GameObject disk)
        {
            for(int i = 0;i < used.Count; i++)
            {
                if (disk.GetInstanceID() == used[i].gameObject.GetInstanceID())
                {
                    used[i].gameObject.SetActive(false);
                    free.Add(used[i]);
                    used.Remove(used[i]);
                    break;
                }
            }
        }
    }

    飞碟游戏场景控制器

    • FirstController

      游戏有三种状态,游戏开始,游戏中,游戏结束。(最好用枚举吧,我用了三个bool变量…)。游戏开始:设置一个定时器,定时从工厂那里获取飞碟,并且发送飞碟,一次只拿一个并立即发送。(有看过师兄的代码是一次就从工厂拿多个,然后慢慢发送飞碟,如果飞碟完了又一次性拿多个)。游戏中:根据定时器,时间到了就获取飞碟并发送。当分数到达10分,将会多增加一种飞碟随机发送这两种并且缩短发送间隔,达到25分则三种飞碟随机发送,发送概率不一样。游戏结束:显示最高分,提供重新开始按钮。

      • 用户点击屏幕发送射线

        如果打中则激活爆炸粒子效果,一段时间后,飞碟工厂再回收。这里使用了协程的概念,我觉得和多线程的意思很像,StartCoroutine开启一个协程,StartCoroutine后面的代码和新的协程一起执行,使用yield暂停协程的执行,yield return的值是代表什么时候继续协程的执行,这样在yield return后面的代码将延迟一点时间执行。更详细的解释请看官方文档。(ps: 这里有一点迷的就是,加上协程后,用户点击一次屏幕会执行两次Hit函数,所以我用了判断物体是否被打中,打中了就不要再继续执行后面代码)

      • 发送飞碟

        从飞碟队列中取出一个飞碟,然后重新设置它的位置(因为拿到的可能是使用过的飞碟)。发送的时候检测未射中的飞碟列表,是否已经飞出镜头外了,如果是用户减一条生命。这里直接调用了user_gui的方法,其实有点违背了MVC,所以如果要改进可以让计分员记录生命,这样可以合并为一个场景中专门记录数值的类。

    部分代码如下:

     void Start ()
     {
         SSDirector director = SSDirector.GetInstance();     
         director.CurrentScenceController = this;             
         disk_factory = Singleton<DiskFactory>.Instance;
         score_recorder = Singleton<ScoreRecorder>.Instance;
         action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
         user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;
     }
     //发射射线
    public void Hit(Vector3 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay(pos);
        RaycastHit[] hits;
        hits = Physics.RaycastAll(ray);
        bool not_hit = false;
        for (int i = 0; i < hits.Length; i++)
        {
            RaycastHit hit = hits[i];
            //射线打中物体
            if (hit.collider.gameObject.GetComponent<DiskData>() != null)
            {
                //射中的物体要在没有打中的飞碟列表中
                for (int j = 0; j < disk_notshot.Count; j++)
                {
                    if (hit.collider.gameObject.GetInstanceID() == disk_notshot[j].gameObject.GetInstanceID())
                    {
                        not_hit = true;
                    }
                }
                if(!not_hit)
                {
                    return;
                }
                disk_notshot.Remove(hit.collider.gameObject);
                //记分员记录分数
                score_recorder.Record(hit.collider.gameObject);
                //显示爆炸粒子效果
                Transform explode = hit.collider.gameObject.transform.GetChild(0);
                explode.GetComponent<ParticleSystem>().Play();
                //等0.08秒后执行回收飞碟
                StartCoroutine(WaitingParticle(0.08f, hit, disk_factory, hit.collider.gameObject));
                break;
            }
        }
    }
    //暂停几秒后回收飞碟
    IEnumerator WaitingParticle(float wait_time, RaycastHit hit, DiskFactory disk_factory, GameObject obj)
    {
        yield return new WaitForSeconds(wait_time);
        //等待之后执行的动作  
        hit.collider.gameObject.transform.position = new Vector3(0, -9, 0);
        disk_factory.FreeDisk(obj);
    }
    
    //发送飞碟
    private void SendDisk()
    {
        float position_x = 16;                       
        if (disk_queue.Count != 0)
        {
            GameObject disk = disk_queue.Dequeue();
            disk_notshot.Add(disk);
            disk.SetActive(true);
            //设置被隐藏了或是新建的飞碟的位置
            float ran_y = Random.Range(1f, 4f);
            float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
            disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
            Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
            disk.transform.position = position;
            //设置飞碟初始所受的力和角度
            float power = Random.Range(10f, 15f);
            float angle = Random.Range(15f, 28f);
            action_manager.UFOFly(disk,angle,power);
        }
    
        for (int i = 0; i < disk_notshot.Count; i++)
        {
            GameObject temp = disk_notshot[i];
            //飞碟飞出摄像机视野也没被打中
            if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true)
            {
                disk_factory.FreeDisk(disk_notshot[i]);
                disk_notshot.Remove(disk_notshot[i]);
                //玩家血量-1
                user_gui.ReduceBlood();
            }
        }
    }

    一般射击都是从点击处射出子弹,带有碰撞器,当碰撞器与飞碟碰撞器触碰时候,应该是开启子弹上的粒子效果才对,然后播放完毕后子弹消失。


    记分员

    • ScoreRecorder

    记录分数和重置分数

    public class ScoreRecorder : MonoBehaviour
    {
        public int score;                   //分数
        void Start ()
        {
            score = 0;
        }
        //记录分数
        public void Record(GameObject disk)
        {
            int temp = disk.GetComponent<DiskData>().score;
            score = temp + score;
            //Debug.Log(score);
        }
        //重置分数
        public void Reset()
        {
            score = 0;
        }
    }

    游戏实现截图

    开始界面

    游戏中

    游戏结束

    小结

    这次游戏使用工厂对象实现了预制体实例化后的重用,提高了游戏性能,还加入了获取鼠标输入,增加了与用户的交互。在写博客的过程中发现了很多代码中存在的问题,也发现了许多游戏中需要改进的地方。

    完整项目请点击传送门,Assets/Scenes/中的myScene是本次游戏场景

    展开全文
  • 基本概念 游戏物体,是所有游戏中最重要的一个概念,所有出现在游戏中的物体,对玩家或者二次开发者可见的,就是游戏物体。 在魔兽争霸3的 Word Editor 的物体编辑器中可以看到有如下几种基本物体 ...

    基本概念

    游戏物体,是所有游戏中最重要的一个概念,所有出现在游戏中的物体,对玩家或者二次开发者可见的,就是游戏物体。

    在魔兽争霸3的 Word Editor 的物体编辑器中可以看到有如下几种基本物体

    • 单位
    • 物品
    • 可破坏物
    • 技能
    • 魔法效果/特效
    • 升级

    这里写图片描述

    这些物体出现在单位的状态栏,建筑的升级栏,或者是商店的购买栏,或者是掉落在地上的物品。

    可以看到这些都是组成游戏的基本元素,游戏内所有交互都是基于这些基本物体之间的。

    而魔兽争霸3的 JASS 脚本以及 JASS 脚本封装一层后带有 GUI 的触发编辑器所进行的二次开发,都是基于暴雪所提供的魔兽争霸3的一些 API 。

    这里写图片描述

    在设计游戏的基本物体的时候,我们就需要考虑,这个物体是什么(IsA),这个物体有什么(HasA),这个物体能做什么(CanDo)。

    同时我们要从这个物体给外界提供出接口,以方便外界组合出新功能。比如对某个物体提供了设置坐标的接口提供之后,便可以配合定时器做出冲锋,位移等效果。

    设计物体

    首先是IsA,开发游戏,大部分是需要选用面向对象语言的,比较符合游戏开发的需求。IsA是面向对象中重要的思想,IsA 决定了这个物体的基本属性。

    举个例子:大法师是一个英雄,是一个单位。那么他就会继承所有单位的属性,同时,所有单位的操作也对大法师有效,比如杀死一个单位的操作,也是可以杀死一个大法师的。

    这里写图片描述

    其次是 HasA,HasA着重表示他拥有什么属性,IsA着重表示了他的父类是谁,那么HasA表示了他拥有了哪些属性。

    大法师有名字,图标,攻击投射物,技能等属性,其中部分属性是从单位类继承而来。

    当然在魔兽争霸3的世界编辑器中,这里的游戏物体显得比较扁平化

    (可以看到在世界编辑器这里分类出来的游戏物体是非常特异化的,比如技能和单位,是几乎无法找到共同属性的,这也是为什么它俩要分开,但是在游戏引擎底层,应该会是属于同一个类,因为都要进行资源的读取,加载以及释放)

    特异化

    魔兽争霸3中的单位,大部分具有相同的属性,比如会有右边的操作栏,生命值,移动速度等等。而魔兽争霸3还分有:召唤物,建筑,普通单位,英雄等概念。

    如果从编辑器这里的角度来看,魔兽争霸3做到特异化这些单位的方法,便是给属性设置不同的值。

    从这里看起来很扁平化的原因便是如此。其实考虑到英雄可能是个召唤物,英雄也可能是个建筑这些概念的话,用属性控制特异化而不是用IsA来控制特异化是比较好的选择。因为现在支持虚继承,支持多重继承的面向对象语言不多了(其实有C++一个也就够了)

    操作栏是魔兽争霸3中能够直接让玩家感受到不同的区域,英雄单位通过技能的不同组合,获得了特异的能力。

    展开全文
  • Unity3D_2D游戏实例从零讲起(3)——基本菜单UI的实现   游戏除了基本的画面渲染,操作对象等等,还需要各种UI菜单来辅助玩家,或是引导,或是做游戏设置。比如,登录菜单,图片的显示,人物血条等等。  游戏...
  • 下面是根据以前自己学习的时候学习“深蓝色右手”WPF游戏教程的“WPF/Silverlight动画及游戏系列教程”,先结合Unity3d技术改编的原理文字 动态实现2D人物角色动画目前有两种主流方法,下面我会分别进行介绍。 ...
  • 系列目录 【Unity3D基础】让物体动起来①--基于UGUI的鼠标点击移动 【Unity3D基础】让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoTween...时光煮雨 Unity3D实现2D人物动画② Un...
  • unity3d_坦克射炮弹

    2018-05-13 22:08:57
    首先创建一个坦克模型,在炮口处创建一个空物体作为炮弹的创建点和出现的点.把下面的代码挂到空物体上.using UnityEngine;using System.Collections;/// &lt;summary&gt;/// 发射子弹/// &...
  • 五、动画和行为 在前面,我们创建了可以自动寻路的敌人角色,接下来我们为其增加动画效果。敌人共有4种动画,对应4种状态——待机、行走、攻击和死亡。 1. 在场景中选择敌人,在Inspector窗口的Animator组件中,取消...
  • unity3d5.0 +UGUI做一个很有用的游戏计时器 一、源代码 [csharp] view plaincopyprint? ///  /// 时间:2014-4-8  /// 作者:GWL  /// 描述:这个类用来处理计时功能,包括计时开始,暂停计...
  • 对于AI,我的初始想法非常easy,首先他要能动,而且是在地图里面动。 懂得撞墙后转弯,然后懂得射击,其它的没有了,基于这个想法,我首先创建了一个MyTank类,用于管理玩家的坦克的活动,然后创建AITank类,AITank...
  • Unity3D插件大全

    2019-05-07 00:26:04
    【转】...200个插件免费分享约5G。 【清单如下】 2D_Toolkit_1.51动画开发插件包 FingerGestures触摸插件 ORK_Okashi_RPG_Kit Unity3D的角色扮演游戏开发工具包 uScript视觉脚本工具Un...
  • Unity 3D插件大全

    2013-10-13 21:09:06
    200个插件免费分享约5G。...ORK_Okashi_RPG_Kit Unity3D的角色扮演游戏开发工具包 uScript视觉脚本工具UnityEasyMotion2D脚本插件 Playmaker_1.21可视化编程插件 RageSpline EZ GUI 2 GlyphDesign
  • 原文:How to make a Power-Up System in Unity 作者:Kevin Small 译者:kmyhy 如果音速小子中没有金色戒指和电动鞋,超级马里奥中没有了蘑菇,或者吃豆人中没有强力豆会是什么样子呢?游戏就不会那么有趣...
1 2 3 4 5 ... 9
收藏数 169
精华内容 67