2019-09-30 12:28:18 Tifinity 阅读数 38
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Unity3D作业四:游戏对象与图形基础

基本操作与演练

先随便看看Fantasy Skybox里有什么,直接进入Demo,在Terrain里可以看到制作好的地形,Prefabs里有许多植物,Mesher是他们的网格,Sprites是按钮,Textures是贴图。

可以创建地形,也可以直接将预制地形拖到场景中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fr5LIG6E-1569817601942)(T:\TH\大三上\3D游戏设计\4游戏对象与图形基础\基本操作演练\images\地形.jpg)]

在检查器里选择地形组件的第二个按钮,绘制树,下方点击编辑树->添加树,将Prefabs中你喜欢的植物拖到新出现窗口的Tree Prefabs中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NvwaHeFj-1569817601943)(T:\TH\大三上\3D游戏设计\4游戏对象与图形基础\基本操作演练\images\地形组件.jpg)]

然后鼠标会变成一个刷子,单击放置树,按住Shift单击删除,设置中的参数都很好理解,大量放置树就是在整个地图上放置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZg69cpK-1569817601943)(T:\TH\大三上\3D游戏设计\4游戏对象与图形基础\基本操作演练\images\放树.jpg)]

在Demo同级的Materials文件夹中有天空盒素材,是传统的六面天空盒,直接将其拖到场景中就可以看到天空。

牧师与魔鬼->?

材料与渲染->?

2019-10-18 22:07:24 hkiller1999 阅读数 12
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Unity3d作业(六)

本次作业的内容为:改进飞碟(Hit UFO)游戏

游戏内容要求:
1、按 adapter模式 设计图修改飞碟游戏
2、使它同时支持物理运动与运动学(变换)运动

1、按 adapter模式 设计图修改飞碟游戏
我们首先需要知道和了解Adapter模式:
Adapter模式分为两种:
1.类适配器模式
2.委托适配器
我们这里更多使用的是类适配器模式。
适配器模式(Adapter)包含以下主要角色。
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
我们如果要在原来的基础上实现物理效果,必须明确谁是适配器谁是适配者。我们过去已经将飞碟游戏的运动功能放在了CCActionManager.cs上,所以这里是非物理学运动,我们要进行添加。在不删除原有的非物理学变换运动动作管理器,可以通过适配器模式来实现。
关于本次游戏的改动具体如图所示:

在这里插入图片描述
在原有代码的基础上增加一个PhysisActionManager动作管理器类,

public class PhysicsActionManager : SSActionManager, ISSActionCallback, IActionManager {
    public FirstSceneController sceneController;
    public List<PhysicsEmitAction> seq = new List<PhysicsEmitAction>();
    public UserClickAction userClickAction;
    public DiskFactory disks;

    protected void Start()
    {
        sceneController = (FirstSceneController)SSDirector.getInstance().currentSceneController;
        sceneController.actionManager = this;
        disks = Singleton<DiskFactory>.Instance;
    }
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
        ....
    }
    public void CheckEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
    }
    public void Pause()
    {
       .......
    }
    public void PlayDisk()
    {
    ......
    }
}

这里使用了动作类为PhysisActionMove动作类。
同时将刚体Rigidbody组件为游戏对象提供物理属性,让游戏对象在场景中可以受到物理引擎的作用。当游戏对象添加了Rigidbody组件后,便使物体具有了相应的物理力。

同时这里我们使用IActionManger作为接口:

public interface IActionManager {
    void PlayDisk();
    void Pause();
}

CCActionManager修改Update函数为PlayDisk函数。只需要修改名字即可,其余不变。
FirstSceneController函数修改三个地方:

FirstSceneController:
//public CCActionManager actionManager修改为:
public IActionManager actionManager;
this.gameObject.AddComponent<CCActionManager>()修改为:
this.gameObject.AddComponent<PhysicsActionManager>();
public void Update()
{
    if (times < 30 && flag == 0)
    {
        if (interval <= 0)
        {
            interval = Random.Range(3, 5);
            times++;
            df.GenDisk();
        }
        interval -= Time.deltaTime;
    }
    //ADD
    actionManager.PlayDisk();
}

接下来实现PhysicsEmitAction:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PhysicsEmitAction : SSAction {
    public Vector3 speed;

    public static PhysicsEmitAction GetSSAction()
    {
        PhysicsEmitAction action = CreateInstance<PhysicsEmitAction>();
        return action;
    }
    public override void Start()
    {
    }
    public override void Update()
    {
        if (transform.position.y < -10 || transform.position.x <= -20 || transform.position.x >= 20)
        {
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
            gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;
            transform.position = Vector3.down;
            callback.SSActionEvent(this);
        }
    }
}

这里PhysicsEmitAction实现了对飞碟物理属性,例如速度等的控制。
具体实现请移步github。
项目地址
参考资料:
pmlpml的github
参考项目
参考课件

2017-03-27 19:18:11 PalytoxinRK 阅读数 531
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

题目:

编程实践,请写步骤,贴代码并解释:

  • 写个用鼠标打飞碟的游戏。

  • 游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量可以变化。

    游戏过程中,仅能创建 n 个飞碟, 且不容许初始化阶段生成任何飞碟。 飞碟线路计算请使用 mathf 类。 向下加速度 a 是常数。 飞碟被用户击中,则回收。并按你定义的规则计算分数。飞碟落地(y < c),则自动回收。

  • 请画 UML 图,描述主要对象及其关系


回答:

这次作业本应该使用单实例工厂化完成的,不过由于我对单实例工厂化的理解不够深入,所以没有采用,而是准备再下一次的作业当中完成对于类的修改。在这个作业中,我将 SceneController作为联系各个类的中间类,并且是单实例的,在这个类中,我连接了其他的所有类,并且再其他所有类中都引用了这个类。其他所有类都继承monobehaviou,因此也是单实例的,各个类之间,只需要通过 SceneController这个中间类,就可以拿到所需要类的内容,为了简化处理,使用了公有变量。



using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;
using UnityEngine.UI;
using rule;

public class UserControl : MonoBehaviour
{

    public Text Main_Text;   // 显示主提示
    public Text Score_Text;  // 显示得分   
    public Text Level_Text;  // 显示回合  
    public SceneController SceneCon;
    public Camera cam;
    public bool IsFinish = false; 

    void Start()
    {
        SceneCon = SceneController.GetInstance();
        SceneCon.UserC = this;
        Main_Text.text = "Gaming";
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown("space") && IsFinish == false)
        {
           SceneCon.GameC.Launch_UFO();
        }
        else if (Input.GetMouseButtonDown(0))
        {
            Vector3 mp = Input.mousePosition;
            Ray ray = cam.GetComponent<Camera>().ScreenPointToRay(mp);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.gameObject.tag == "UFO")
                {
                    SceneCon.GameC.Hit_UFO(hit.transform.gameObject);
                    SceneCon.rule.HitUFO();
                }
            }
        }

        Level_Text.text = "Level: " + SceneCon.Now_Level;
        Score_Text.text = "Score: " + SceneCon.Now_Score;
    }



}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;




namespace MyGame
{

    public class UFOControl : MonoBehaviour
    {
        public List<GameObject> UFO_Ready_List;
        public List<GameObject> UFO_Active_List;   
        public List<GameObject> UFO_UnActive_List;
        public GameObject UFO_Pre;  //  UFO预制
        private Vector3 UnusedPlace = new Vector3(1000f, 1000f, 1000f);

        public SceneController scene;

        public void Start()
        {
            scene = SceneController.GetInstance();
            scene.ufoc = this;

            for(int i=0; i<10;i++)
            {
                GameObject Temp = GameObject.Instantiate(UFO_Pre, UnusedPlace, Quaternion.identity) as GameObject;
                UFO_Ready_List.Add(Temp);
            }

        }




        //函数,函数
        //发射判断
        
        public void Launch_Ready_UFO()
        {
            if(UFO_Ready_List.Count  == 0)
            {
                scene.NextLevel();
                for(int i = 0; i< 10; i++)
                {
                    if(UFO_Active_List.Count !=0)
                    {
                        GameObject temp = UFO_Active_List[0];
                        temp.transform.position = UnusedPlace;
                        UFO_Ready_List.Add(temp);
                        UFO_Active_List.Remove(temp);
                    }
                    else if(UFO_UnActive_List.Count !=0)
                    {
                        GameObject temp = UFO_UnActive_List[0];
                        temp.transform.position = UnusedPlace;
                        UFO_Ready_List.Add(temp);
                        UFO_UnActive_List.Remove(temp);
                    }
                }
            }
        }
        


    }
}

using MyGame;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TimeControl : MonoBehaviour
{

    public UFOControl UFOCon;
    private float time;

    public float UFO_Speed;
    public SceneController scene;
    public float UFO_Speed_y;
    public float UFO_Speed_x;
    // Use this for initialization
    void Start()
    {
        scene = SceneController.GetInstance();
    }

    public void setting(float Speed)
    {
        time = Time.time;
        UFO_Speed = Speed;
  

    }

    // Update is called once per frame
    void Update()
    {

        UFO_Speed_y = UnityEngine.Random.Range(0, 3) + UFO_Speed;
        UFO_Speed_x = UnityEngine.Random.Range(-10, 10) + UFO_Speed;
        transform.Translate(Vector3.down * Time.deltaTime*UFO_Speed_y, Space.World);
        transform.Translate(Vector3.left * Time.deltaTime*UFO_Speed_x);

        if (this.gameObject.transform.position.y < -3)
        {
            scene.rule.MissUFO();
        }

        if (Time.time - time > 8 || this.gameObject.transform.position.y < -3)
        {
            scene.ufoc.UFO_Active_List.Remove(this.gameObject);
            scene.ufoc.UFO_UnActive_List.Add(this.gameObject);
            this.gameObject.transform.position = new Vector3(1000f, 1000f, 1000f);
            Destroy(this);
        }
    }
}


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;
using rule;

namespace MyGame
{


    public class SceneController : System.Object
    {
        private static SceneController _instance;
        
        public int Now_Score;   //当前分数
        public Level Now_Level = Level.Ready;//当前关卡



        public GameControl GameC; //和GameControl联通
        public UserControl UserC;   //  和UserControl联通
        public Rule rule;   //和Rule联通
        public UFOControl ufoc; //和UFOControl联通


        public static SceneController GetInstance()
        {
            if (_instance == null)
            {
                _instance = new SceneController();
            }
            return _instance;
        }

        //判断是否进入下一个LEVEL
        public void NextLevel()
        {
            if (Now_Level == Level.Ready)
                rule.Load_Reources(Now_Level);
            else if (Now_Level == Level.First)
                rule.Load_Reources(Now_Level);
            else if (Now_Level == Level.Second)
                rule.Load_Reources(Now_Level);
            else if (Now_Level == Level.Tird)
            {
                if(Now_Score > 0)
                {
                    UserC.IsFinish = true;
                    UserC.Main_Text.text = "win";
                }
                else
                {
                    UserC.IsFinish = true;
                    UserC.Main_Text.text= "Fail";
                }
            }
        }

    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;
using rule;

public class GameControl : MonoBehaviour
{

    public Color UFO_Color;                // 飞碟颜色  
    public float UFO_Speed;                // 发射速度  
    public SceneController scene;


    void Awake()
    {
        scene = SceneController.GetInstance();
        scene.GameC = this;
        
    }
    //初始化场景
    public void setting(Color color, float speed)
    {
        UFO_Color = color;
        UFO_Speed = speed;
    }

    // Use this for initialization
    void Start()
    {

    }

    public void Launch_UFO()
    {
            int i = 0;
        if (scene.Now_Level == Level.First)
            i = 1;
        else if (scene.Now_Level == Level.Second)
            i = 2;
        else if (scene.Now_Level == Level.Tird)
            i = 5;

        for(int j = 0; j < i;j++)
        {
            scene.ufoc.Launch_Ready_UFO();
            GameObject Temp = scene.ufoc.UFO_Ready_List[0];
            scene.ufoc.UFO_Ready_List.Remove(Temp);
            Temp.GetComponent<Renderer>().material.color = UFO_Color;
            TimeControl ac = Temp.AddComponent<TimeControl>();
            Temp.transform.position = new Vector3(0+Random.Range(-10,10), 8+Random.Range(-4,5), 0);
            ac.setting(UFO_Speed);
            scene.ufoc.UFO_Active_List.Add(Temp);

        }

    }

    public void Hit_UFO(GameObject temp)
    {
        scene.ufoc.UFO_Active_List.Remove(temp);
        scene.ufoc.UFO_UnActive_List.Add(temp);
        temp.transform.position = new Vector3(1000f, 1000f, 1000f);
       
    }



    // Update is called once per frame
    void Update()
    {

    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;

namespace rule
{
    public enum Level { Ready, First, Second, Tird }; //关卡信息
    public class Rule : MonoBehaviour
    {

        public Color UFO_Color;                //颜色
        public int UFO_Number = 10;         // 飞碟一共的数量
        public int UFO_Speed;               //飞碟的速度
        public int OneUFOScore = 1;     //击中飞碟得到的分数
        public int OneUFOFail = 1;       //飞碟掉落失去的分数


        public SceneController scene;

        void Awake()
        {
            scene = SceneController.GetInstance();
            scene.rule = this;
        }

        void Start()
        {
            scene.NextLevel();
        }

        // 击中飞碟得分  
        public void HitUFO()
        {
            scene.Now_Score += OneUFOScore;
            Debug.Log("GODDDDDDDDDDDDD" + scene.Now_Score);
        }

        // 没击中飞碟失分  
        public void MissUFO()
        {
            scene.Now_Score -= OneUFOScore;
            Debug.Log("MISSSSSSSSSSSSSS" + scene.Now_Score);
        }

        public void Load_Reources(Level level)
        {
            if (level == Level.Ready)
            {
                UFO_Color = Color.blue;
                UFO_Speed = 1;
                scene.Now_Level = Level.First;
                scene.GameC.setting(UFO_Color, UFO_Speed);
            }
            else if (level == Level.First)
            {
                UFO_Color = Color.green;
                UFO_Speed = 3;
                scene.Now_Level = Level.Second;
                scene.GameC.setting(UFO_Color, UFO_Speed);
            }
            else if (level == Level.Second)
            {
                UFO_Color = Color.red;
                UFO_Speed = 5;
                scene.Now_Level = Level.Tird;
                scene.GameC.setting(UFO_Color, UFO_Speed);
            }
        }
    }

}



2019-09-22 00:03:57 WeiXiaoAssassin 阅读数 76
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Priests-and-Devils - Unity3D作业


视频地址:HERE

博客地址:HERE

原游戏地址:HERE

作业要求:

整个游戏仅 主摄像机 和 一个 Empty 对象, 其他对象由代码动态生成

整个游戏不许出现 Find 游戏对象, SendMessage 这类突破程序结构的 通讯耦合 语句

列出游戏中提及的事物(Objects)

  • Priest
  • Devil
  • Boat
  • Other environmental objects —— RiverRiverbank

用表格列出玩家动作表(规则表),注意,动作越少越好

动作 参数 结果
启动游戏 初始界面
开始游戏(初始界面) 游戏界面
重新开始(游戏界面) 重新加载游戏界面资源
点击对象(游戏界面) 对象 对象根据当前位置移动
返回(游戏界面) 初始界面

请将游戏中对象做成预制

prefabs

  • Boat -> Boat对象
  • Devil -> Devil对象
  • Priest -> Priest对象
  • River -> River对象
  • Solid -> Riverbank对象

MVC 结构程序

面向对象设计的核心:基于职责的设计

即:模拟人类组织管理社会的方法,根据不同人拥有资源、知识与技能的不同,赋予不同人(或对象)特定的职责。再按一定结构(如设计模式),将它们组织起来。

先直接给出游戏框架设计:

frame

整个程序都是基于该架构图实现

结构分析

  • 导演 (SSDriector) —— 1个

    //把握全局;控制场景;负责场景的切换
    public class SSDirector : System.Object
    {
        private static SSDirector _instance;
        
        public ISceneController currentSceneController { get;set;}
    
        public bool running{ get; set;}
    
        public static SSDirector getInstance(){
            if(_instance == null ){
                _instance = new SSDirector();
            }
            return _instance;
        }
    
        public void LoadScene(int num){
            SceneManager.LoadScene(num);
        }
        public int getFPS(){
            return Application.targetFrameRate;
        }
    
        public void setFPS(int fps){
            Application.targetFrameRate = fps;
        }
    }
    
  • 场景(Scene)-> 场记(ISceneController)—— 2个

    scenes

    抽象对象:ISceneController

    //加载资源,接受点击事件
    public interface ISceneController
    {
        void LoadResources();
        void getClick(Character.Type objType,int id);
    }
    

    具体对象:GameSceneControllerHomeSceneController

    //负责游戏资源的加载,游戏进程胜负的判断,计时器等资源,点击事件管理
    //提供接口给GUI界面(View)
    public class GameSceneController : MonoBehaviour,ISceneController,IUserAction{
       void Awake() {
           SSDirector director = SSDirector.getInstance();
           director.setFPS(60);
           director.currentSceneController = this;
           this.gameObject.AddComponent<UserGUI>();
           director.currentSceneController.LoadResources();
       }
       void LoadResources(){}
       void getClick(Character.Type objType,int id){}
       void checkMove(Character.Type objType,int id){}
       bool checkLose(){}
       bool checkWin(){}
       void restart(){}
       void back(){}
       GameSceneController.GameStatus getCurStatus(){}
       IEnumerator waitForOneSecond(){}
       int getTimer(){}
    }
    
    //负责添加初始界面(HomeGUI)组件,接受按钮事件进行场景的切换
    public class HomeSceneController : MonoBehaviour,ISceneController,IHomeAction{
        void Awake() {
            SSDirector director = SSDirector.getInstance();
            director.setFPS(60);
            director.currentSceneController = this;
            director.currentSceneController.LoadResources();
            this.gameObject.AddComponent<HomeGUI>();
        }
        void startGame(){}
        void finish(){}
        void LoadResources(){}
        void getClick(Character.Type objType,int id){}
    }
    
  • GameObject对象(Model)的管理

    • 共同点:

      位置 / 对象属于哪种GameObject:

      public class Character{
          public enum Status{leftLand,leftBoat,rightBoat,rightLand};
          public enum Type{classPriest,classDevil,classBoat};
      }
      

      移动:

      通过事先设置的 Vector3 确定该对象在不同状态的位置

      public class Devil{
          public static Vector3[] boatPos = new Vector3[2]{new Vector3(-1.1f,0.05f,-5.5f),new Vector3(-1.7f,0.05f,-5.5f)};
      }
      
      public class Priest{
          public static Vector3[] boatPos = new Vector3[2]{new Vector3(-1.1f,0.2f,-5.5f),new Vector3(-1.7f,0.2f,-5.5f)};
      }
      
      public class Boat{
          public static Vector3[] boatPos = new Vector3[2]{new Vector3(-1.5f,-0.2f,-5.5f),new Vector3(1.5f,-0.2f,-5.5f)};
      }
      
    • 区别:

      • PriestDevil都需要设置从船上到岸上/岸上到船上的移动
      • Boat 需要负责为PriestDevil分配船上的座位

    根据它们职责的不同在 Model 层写好对应的函数

  • View管理

    View界面需要定义好用户动作的接口,再实现对应的GUi页面

    • IUserAction/IHomeAction

      public interface IUserAction
      {
          void restart();			//重新开始
          void back();			//回到初始界面
          GameSceneController.GameStatus getCurStatus();	//返回当前游戏状态
          int getTimer();			//计时器显示 接口
      }
      
      public interface IHomeAction
      {
          void startGame();		//开始游戏
          void finish();			//退出游戏
      }
      
    • 对应的GUI页面 UserGUI/HomeGUI

      共同点:

      • 在OnGUI()中增加button/Label的GUI控件
      • 根据获取Controller中的游戏状态(接口),达到显示不同界面的效果
      • 通过获取SSDirector单实例实现将当前场景的控制器作为IuserAction/IHomeAction从而达到获取控制器的接口的效果
  • 自定义组件

    ToMove组件,使游戏对象的移动不是瞬移,而是渐进的(MoveTowards)

    其中最重要的Update()函数

    private void Update() {
            if(moveStatus == MoveStatus.ToDest){
                this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, destination, moveSpeed * Time.deltaTime);
                    if (this.gameObject.transform.position == destination){
                        moveStatus = MoveStatus.Stationary;
                    }
            }
            if(moveStatus == MoveStatus.ToMiddle){
                this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, middle, moveSpeed * Time.deltaTime);
                    if (this.gameObject.transform.position == middle){
                        moveStatus = MoveStatus.ToDest;
                    }
            }
        }
    

    ButtonSimulation 组件,使游戏对象可以被点击,从而使控制器接收到点击事件

    public class ButtonSimulation : MonoBehaviour
    {
        public Character.Type objType;
        public int id;
    
        void OnMouseDown(){
            Debug.Log("OK!!");
            SSDirector.getInstance().currentSceneController.getClick(objType,id);
        }
    
        void Start(){}
    
        void Update(){}
    }
    

游戏界面

game

2019-10-08 21:25:10 chentf5 阅读数 34
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Unity3d作业||与游戏世界交互

编写一个简单的鼠标打飞碟(Hit UFO)游戏

  • 游戏内容要求:
    1. 游戏有 n 个 round,每个 round 都包括10 次 trial;
    2. 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
    3. 每个 trial 的飞碟有随机性,总体难度随 round 上升;
    4. 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
  • 游戏的要求:
    • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
    • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离

工厂类模式

  • 需要进行频繁的游戏对象的产生和销毁,使用工厂类来减少销毁次数
  • 用工厂方法代替new操作的一种模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。**注意事项:**作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

代码实现

  • 工厂类

    使用工厂类减少游戏的开销,采用工厂模式对飞碟进行管理,进行飞碟的产生和回收

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using MyGame;
    
    public class DiskFactory : MonoBehaviour
    {
        private static DiskFactory _instance;
        public FirstControl sceneControler { get; set; }
        GameObject diskPrefab;
        public DiskControl diskData;
        public List<GameObject> used;//使用队列
        public List<GameObject> free;//释放队列
        // Use this for initialization
    	//得到实例
        public static DiskFactory getInstance()
        {
            return _instance;
        }
    
        private void Awake()
        {
            if (_instance == null)
            {
                _instance = Singleton<DiskFactory>.Instance;
                _instance.used = new List<GameObject>();
                _instance.free = new List<GameObject>();
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk"), new Vector3(40, 0, 0), Quaternion.identity);
            }
            Debug.Log("instance: " + _instance);
        }
        public void Start()
        {
            sceneControler = (FirstControl)Director.getInstance().sceneCtrl;
            Debug.Log(sceneControler);
            //Debug.Log(this);
            //Debug.Log(_instance);
            sceneControler.factory = _instance;
            Debug.Log("DiskFactory: factory");
            //Debug.Log(sceneControler.factory);        
        }
    	//产生新飞碟
        public GameObject getDisk(int round)
        {
            if (sceneControler.scoreRecorder.Score >= round * 10)
            {
                if (sceneControler.user.round < 3)
                {
                    sceneControler.user.round++;
                    sceneControler.user.num = 0;
                    sceneControler.scoreRecorder.Score = 0;
                }
                else
                {
                    sceneControler.user.game = 2;
                }
            }
            else
            {
                if (sceneControler.user.num >= 10)
                {
                    sceneControler.user.game = 1;
                }
            }
            GameObject newDisk;
            RoundControl diskOfCurrentRound = new RoundControl(round);
            if (free.Count == 0) // if no free disk, then create a new disk
            {
                newDisk = GameObject.Instantiate(diskPrefab) as GameObject;
                newDisk.AddComponent<ClickGUI>();
                diskData = newDisk.AddComponent<DiskControl>();
            }
            else // else let the first free disk be the newDisk
            {
                newDisk = free[0];
                free.Remove(free[0]);
                newDisk.SetActive(true);
                Debug.Log("get from free");
            }
            diskData = newDisk.GetComponent<DiskControl>();
            diskData.color = diskOfCurrentRound.color;
            //Debug.Log("free: " + free.Count);
    
            newDisk.transform.localScale = diskOfCurrentRound.scale * diskPrefab.transform.localScale;
            newDisk.GetComponent<Renderer>().material.color = diskData.color;
    
            used.Add(newDisk);
            return newDisk;
        }
    	//释放飞碟
        public void freeDisk(GameObject disk1)
        {
            used.Remove(disk1);
            disk1.SetActive(false);
            free.Add(disk1);
            Debug.Log("free: " + free.Count);
            return;
        }
    	//重新开始
        public void Restart()
        {
            used.Clear();
            free.Clear();
        }
    }
    
    
  • 场景控制baseCode

    • 导演类

          public class Director : System.Object
          {
              private static Director _instance;
              public ISceneControl sceneCtrl { get; set; }
      
              public bool playing { get; set; } //
      
              public static Director getInstance()
              {
                  if (_instance == null) return _instance = new Director();
                  else return _instance;
              }
      
              public int getFPS()
              {
                  return Application.targetFrameRate;
              }
      
              public void setFPS(int fps)
              {
                  Application.targetFrameRate = fps;
              }
          }
      
    • 单例模式的模板

      public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
          {
      
              protected static T instance;
              public static T Instance
              {
                  get
                  {
                      if (instance == null)
                      {
                          //instance = new GameObject(typeof(T).Name).AddComponent<T>();
                          instance = (T)FindObjectOfType(typeof(T));
                          if (instance == null)
                          {
                              Debug.LogError("no scene instance");
                          }
                      }
                      return instance;
                  }
              }
          }
      
    • 动作控制器,场景控制器,用户控制器,飞碟工厂控制器,回合控制器的一些接口类

      public interface ISSActionCallback
          {
              void ActionDone(SSAction source);
          }
      
          public interface ISceneControl
          {
              void LoadPrefabs();
              void PlayDisk();
          }
      
          public interface IUserAction
          {
              void Begin();
              void Hit(DiskControl diskCtrl);
              void Restart();
              void SwitchMode();
          }
      
          public class DiskControl : MonoBehaviour
          {
              public float size;
              public Color color;
              public float speed;
              public bool hit = false;
              public SSAction action;
          }
      
          public class RoundControl
          {
              int round = 0;
              public float scale;
              public Color color;
              public RoundControl(int r)
              {
                  round = r;
                  scale = 5 - r;
                  switch (r)
                  {
                      case 1:
                          color = Color.blue;
                          break;
                      case 2:
                          color = Color.red;
                          break;
                      case 3:
                          color = Color.yellow;
                          break;
                  }            
              }
          }
      
          public class RecordControl : MonoBehaviour
          {
              public int Score = 0;//分数
              public FirstControl sceneControler { get; set; }
              // Use this for initialization
              void Start()
              {
                  sceneControler = (FirstControl)Director.getInstance().sceneCtrl;
                  sceneControler.scoreRecorder = this;
              }
              public void add()
              {
                  Score += sceneControler.user.round;
                  sceneControler.user.score = Score;
                  //Debug.Log(Score);
              }
              public void miss()
              {
                  Score -= sceneControler.user.round;
                  sceneControler.user.score = Score;
                  //Debug.Log(Score);
              }
          }
      }
      
  • firstControll总控制

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using MyGame;
    using UnityEngine.SceneManagement;
    
    public class FirstControl : MonoBehaviour, ISceneControl, IUserAction {
    
        //public ActionManager MyActionManager { get; set; }
        public ActionManagerAdapter myAdapter;
        public DiskFactory factory { get; set; }
        public RecordControl scoreRecorder;
        public UserGUI user;
        public static float time = 0;
        
        void Awake()
        {
            Director diretor = Director.getInstance();
            diretor.sceneCtrl = this;
            //Debug.Log("FirstControl: factory");
            //Debug.Log(factory);
            //Debug.Log(diretor);                                
        }
    
        // Use this for initialization
        void Start()
        {
            Begin();
        }
    	
    	// Update is called once per frame
    	void Update () {
    
        }
    
        void FixedUpdate()
        {
            //Debug.Log("first fixedupdate mode: " + myAdapter.mode);
    
            //Time.fixedDeltaTime = 1;
            time += Time.deltaTime;
            if (time < 1)
                return;
            time = 0;
    
            // if round <= 3 and is playing, 
    
            if (user.round <= 3 && user.game == 0)
            {
                PlayDisk();
                user.num++;
            }
        }
    
        public void LoadPrefabs()
        {
        }
    
        public void Begin()
        {
            LoadPrefabs();
            //MyActionManager = gameObject.AddComponent<ActionManager>() as ActionManager;
            //Debug.Log(MyActionManager);
            myAdapter = new ActionManagerAdapter(gameObject);
            scoreRecorder = gameObject.AddComponent<RecordControl>();
            user = gameObject.AddComponent<UserGUI>();
            user.Begin();
        }
    
        public void Hit(DiskControl diskCtrl)
        {        
            if (user.game == 0)
            {            
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                
                if (Physics.Raycast(ray, out hit))
                {
                    //Debug.Log(hit.GetType());
                    //Debug.Log(hit.transform);
                    hit.collider.gameObject.SetActive(false);
                    //hit.collider.gameObject.transform.position = new Vector3(6 - Random.value * 12, 0, 0);
                    //factory.freeDisk(hit.collider.gameObject);
                    Debug.Log("Hit");
                    hit.collider.gameObject.GetComponent<DiskControl>().hit = true;
                    scoreRecorder.add();
                }
                else
                {
                    Debug.Log("Miss");
                    scoreRecorder.miss();
                }
            }
            //user.status = Check();
        }
        public void PlayDisk()
        {
            //MyActionManager.playDisk(user.round);
            myAdapter.PlayDisk(user.round);
        }
        public void Restart()
        {
            SceneManager.LoadScene("scene");
        }
        public void SwitchMode()
        {
            Debug.Log("Switch Mode");
            myAdapter.SwitchActionMode();
        }
        public int Check()
        {
            return 0;
        }
    }
    
    
  • 用户 UserGUI,点击事件 ClickGUI

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using MyGame;

public class UserGUI : MonoBehaviour {
    private IUserAction action;     
    GUIStyle LabelStyle1;
    GUIStyle LabelStyle2;
    GUIStyle ButtonStyle;
    private int timeLeft = 60;
    public int score = 0;
    public static float time = 0;

    public int round = 1;
    public int CoolTimes = 3; 
    public int game = 0; // status
    public int num = 0; // numbers of disk
    public int mode = 0; // action mode

    // Use this for initialization
    void Start () {
        action = (IUserAction)Director.getInstance().sceneCtrl;

        LabelStyle1 = new GUIStyle();
        LabelStyle1.fontSize = 20;
        LabelStyle1.alignment = TextAnchor.MiddleCenter;

        LabelStyle2 = new GUIStyle();
        LabelStyle2.fontSize = 30;
        LabelStyle2.alignment = TextAnchor.MiddleCenter;

        ButtonStyle = new GUIStyle("Button");
        ButtonStyle.fontSize = 20;
    }

    void FixedUpdate()
    {
        time += Time.deltaTime;
        if (time < 1)
            return;
        time = 0;

        if (game == 3)
        {
            if (CoolTimes > 1) CoolTimes--;
            else game = 0;
        }
        if (game == 0)
        {
            timeLeft--;
        }
    }

    public void Restart()
    {
        timeLeft = 60;
        round = 1;
        CoolTimes = 3;
        game = 3;
        num = 0;
        score = 0;
        mode = 0;
    }

    public void Begin()
    {
        Restart();
    }

    void OnGUI () {
        string str = mode == 0 ? "Normal" : "Physics";
        GUI.Label(new Rect(Screen.width / 2 - 30, 10, 100, 50), "Mode: " + str, LabelStyle1);
        if (GUI.Button(new Rect(20, 20, 100, 50), "Switch", ButtonStyle)) // switch mode
        {            
            action.SwitchMode();
            mode = 1 - mode;
        }
        if (game == 0) // playing
        {
            GUI.Label(new Rect(Screen.width / 2 - 180, Screen.height / 2 - 160, 100, 50), "Round: " + round, LabelStyle1);
            GUI.Label(new Rect(Screen.width / 2 - 30, Screen.height / 2 - 160, 100, 50), "Time: " + timeLeft, LabelStyle1);
            GUI.Label(new Rect(Screen.width / 2 + 120, Screen.height / 2 - 160, 100, 50), "Score: " + score, LabelStyle1);
            if (timeLeft == 0) game = 1;
        }
        else if (game == 1) // game over
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "Gameover!", LabelStyle2);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", ButtonStyle))
            {
                game = 0;
                action.Restart();
            }
        }
        else if (game == 2) // win a round
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You win!", LabelStyle2);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", ButtonStyle))
            {
                game = 0;
                action.Restart();
            }
        }
        else if (game == 3) // ready
        {
            GUI.Label(new Rect(Screen.width / 2 - 30, Screen.height / 2, 100, 50), CoolTimes.ToString(), LabelStyle2);
        }
    }

}

  • ActionControl,基础动作管理器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using MyGame;
    
    public class ActionManager : SSActionManager
    {
        public FirstControl sceneController;
        public DiskFactory diskFactory;
        public RecordControl scoreRecorder;
        public Emit EmitDisk;
        public GameObject Disk;
        int count = 0;
        // Use this for initialization
        protected void Start()
        {
            sceneController = (FirstControl)Director.getInstance().sceneCtrl;
            diskFactory = sceneController.factory;
            scoreRecorder = sceneController.scoreRecorder;
            sceneController.MyActionManager = this;
        }
    
        // Update is called once per frame
        protected new void Update()
        {
            // if round <= 3 and is playing, 
            if (sceneController.user.round <= 3 && sceneController.user.game == 0)
            {
                count++;
                if (count == 60)
                {
                    playDisk(sceneController.user.round);
                    sceneController.user.num++;
                    count = 0;
                }
                base.Update();
            }
        }
    
        public void playDisk(int round)
        {
            //Debug.Log(diskFactory);
            EmitDisk = Emit.GetSSAction();
            Disk = diskFactory.getDisk(round);
            this.AddAction(Disk, EmitDisk, this);
            Disk.GetComponent<DiskControl>().action = EmitDisk;
        }
    
        public void SSActionEvent(SSAction source)
        {
            if (!source.GameObject.GetComponent<DiskControl>().hit)
                scoreRecorder.miss();
            diskFactory.freeDisk(source.GameObject);
            source.GameObject.GetComponent<DiskControl>().hit = false;
        }
    }
    
    public class SSAction : ScriptableObject
    {
        public bool enable = true;
        public bool destroy = false;
    
        public GameObject GameObject { get; set; }
        public Transform Transform { get; set; }
        public ISSActionCallback Callback { get; set; }
    
        public virtual void Start()
        {
            throw new System.NotImplementedException();
        }
    
        public virtual void Update()
        {
            throw new System.NotImplementedException();
        }
    
        //public virtual void FixedUpdate()
        //{
        //    //throw new System.NotImplementedException();
        //}
    }
    
    public class SSMoveToAction : SSAction
    {
        public Vector3 des;
        public float speed;
    
        private SSMoveToAction() { }
    
        public static SSMoveToAction GetSSMoveToAction(Vector3 target, float speed)
        {
            SSMoveToAction action = CreateInstance<SSMoveToAction>();
            action.des = target;
            action.speed = speed;
            return action;
        }
    
        public override void Start() { }
    
        public override void Update()
        {
            Transform.position = Vector3.MoveTowards(Transform.position, des, speed * Time.deltaTime);
            if (Transform.position == des)
            {
                destroy = true;
                Callback.ActionDone(this);
            }
        }
    }
    public class SequenceAction : SSAction, ISSActionCallback
    {
        public List<SSAction> sequence;
        public int repeat = -1;
        public int start = 0;
    
        public static SequenceAction GetSequenceAction(int repeat, int start, List<SSAction> sequence)
        {
            SequenceAction action = CreateInstance<SequenceAction>();
            action.sequence = sequence;
            action.repeat = repeat;
            action.start = start;
            return action;
        }
    
        public override void Update()
        {
            if (sequence.Count == 0) return;
            if (start < sequence.Count)
            {
                sequence[start].Update();
            }
        }
    
        public void ActionDone(SSAction source)
        {
            source.destroy = false;
            start++;
            if (start >= sequence.Count)
            {
                start = 0;
                if (repeat > 0) repeat--;
                if (repeat == 0)
                {
                    destroy = true;
                    Callback.ActionDone(this);
                }
            }
        }
    
        public override void Start()
        {
            foreach (SSAction action in sequence)
            {
                action.GameObject = GameObject;
                action.Transform = Transform;
                action.Callback = this;
                action.Start();
            }
        }
    
        void OnDestroy()
        {
            foreach (SSAction action in sequence)
            {
                DestroyObject(action);
            }
        }
    }
    
    public class SSActionManager : MonoBehaviour, ISSActionCallback
    {
        private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
        private List<SSAction> waitingAdd = new List<SSAction>();
        private List<int> waitingDelete = new List<int>();
    
        protected void Update()
        {
            foreach (SSAction action in waitingAdd)
            {
                actions[action.GetInstanceID()] = action;
            }
            waitingAdd.Clear();
    
            foreach (KeyValuePair<int, SSAction> kv in actions)
            {
                SSAction action = kv.Value;
                if (action.destroy)
                {
                    waitingDelete.Add(action.GetInstanceID());
                }
                else if (action.enable)
                {
                    action.Update();
                    //action.FixedUpdate();
                }
            }
    
            foreach (int key in waitingDelete)
            {
                SSAction action = actions[key];
                actions.Remove(key);
                DestroyObject(action);
            }
            waitingDelete.Clear();
        }
    
        public void AddAction(GameObject gameObject, SSAction action, ISSActionCallback callback)
        {
            action.GameObject = gameObject;
            action.Transform = gameObject.transform;
            action.Callback = callback;
            waitingAdd.Add(action);
            action.Start();
        }
    
        public void ActionDone(SSAction source) { }
    }
    
  • Emit 飞碟动作控制器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using MyGame;
    
    public class Emit : SSAction
    {
        public FirstControl sceneControler = (FirstControl)Director.getInstance().sceneCtrl;
        public Vector3 target;     
        public float speed;     
        private float distanceToTarget;    
        float startX;
        float targetX;
        float targetY;
    
        public override void Start()
        {
            speed = sceneControler.user.round * 5;
            GameObject.GetComponent<DiskControl>().speed = speed;
            startX = 6 - Random.value * 12;
            if (Random.value > 0.5)
            {
                targetX = 36 - Random.value * 36;
            }
            else
            {
                targetX = -36 + Random.value * 36;
            }
            if (Random.value > 0.5)
            {
                targetY = 25 - Random.value * 25;
            }
            else
            {
                targetY = -25 + Random.value * 25;
            }
            //targetY = (Random.value * 25);
            this.Transform.position = new Vector3(startX, 0, 0);
            target = new Vector3(targetX, targetY, 30);
            //计算两者之间的距离  
            distanceToTarget = Vector3.Distance(this.Transform.position, target);
        }
        public static Emit GetSSAction()
        {
            Emit action = ScriptableObject.CreateInstance<Emit>();
            return action;
        }
        public override void Update()
        {
            Vector3 targetPos = target;
    
            //让始终它朝着目标  
            GameObject.transform.LookAt(targetPos);
    
            //计算弧线中的夹角  
            float angle = Mathf.Min(1, Vector3.Distance(GameObject.transform.position, targetPos) / distanceToTarget) * 45;
            GameObject.transform.rotation = GameObject.transform.rotation * Quaternion.Euler(Mathf.Clamp(-angle, -42, 42), 0, 0);
            float currentDist = Vector3.Distance(GameObject.transform.position, target);
            GameObject.transform.Translate(Vector3.forward * Mathf.Min(speed * Time.deltaTime, currentDist));
            if (this.Transform.position == target)
            {
                Debug.Log("Destroy");
                //DiskFactory.getInstance().freeDisk(gameobject);
                GameObject.SetActive(false);
                GameObject.transform.position = new Vector3(startX, 0, 0);
                sceneControler.factory.freeDisk(GameObject);
                this.destroy = true;
                this.Callback.ActionDone(this);
            }
        }
    }
    
  • 添加天空盒

实验结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxCdaUO5-1570541079843)(1.PNG)]

GameObject.transform.rotation * Quaternion.Euler(Mathf.Clamp(-angle, -42, 42), 0, 0);
float currentDist = Vector3.Distance(GameObject.transform.position, target);
GameObject.transform.Translate(Vector3.forward * Mathf.Min(speed * Time.deltaTime, currentDist));
if (this.Transform.position == target)
{
Debug.Log(“Destroy”);
//DiskFactory.getInstance().freeDisk(gameobject);
GameObject.SetActive(false);
GameObject.transform.position = new Vector3(startX, 0, 0);
sceneControler.factory.freeDisk(GameObject);
this.destroy = true;
this.Callback.ActionDone(this);
}
}
}




- 添加天空盒





### 实验结果

![\[外链图片转存中...(img-IxCdaUO5-1570541079843)\]](https://img-blog.csdnimg.cn/20191008212504463.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW50ZjU=,size_16,color_FFFFFF,t_70)



Unity3d作业(五)

阅读数 22

Unity-3D 作业一

阅读数 49

Unity3D作业三

阅读数 57

没有更多推荐了,返回首页