Unity3D学习之打飞碟游戏

  首先,先上设计图:
  这里写图片描述
  效果图:
  这里写图片描述
  预设设置:
  这里写图片描述
  


  
  

  简单说一下设计思路。对于飞碟的运动:运动学运动,有点类似牧师恶魔里面的过河;物理运动,则类似射箭小游戏的设计。所以,对于这两种动作分别实现即可。
  
运动学实现:

 public void playDisk(GameObject disk)
    {
        temp = disk;
        //随机选取起始坐标
        float x = Random.Range(-1.5f, 1.5f);
        float z = Random.Range(-2f, 2f);
        Vector3 initialPos = new Vector3(x, -3.7f, z);
        disk.transform.position = initialPos;


        //随机选取发射方向
        float xEnd = Random.Range(-2f, 2f);
        float yEnd = Random.Range(6f, 8f);
        float zEnd = Random.Range(0f, 5f);
        Vector3 dir = new Vector3(xEnd, yEnd, zEnd);

        //随机设置速度
        float speed = Random.Range(10f,50f);

        action = MoveToAction.getAction(dir, speed);
        RunAction(disk, action, this);
    }
 //飞碟运动到指定地点后,添加重力,方便其落回地面
 public void actionDone(SSAction source)
 {
      if(source == action)
      {
            temp.GetComponent<Rigidbody>().useGravity = true;
      }
 }

物理学实现:

public void playDisk(GameObject disk)
{
    //随机选取起始坐标
    float x = Random.Range(-1.5f, 1.5f);
    float z = Random.Range(0f, 0.5f);
    Vector3 initialPos = new Vector3(x, -3.7f, z);
    disk.transform.position = initialPos;


    //随机选取发射方向
    float xEnd = Random.Range(-2f, 2f);
    float yEnd = Random.Range(6f, 8f);
    float zEnd = Random.Range(0f, 5f);
    Vector3 dir = new Vector3(xEnd, yEnd, zEnd);

    disk.transform.up = dir;
    disk.GetComponent<Rigidbody>().velocity = dir * speed; //设置速度
    disk.GetComponent<Rigidbody>().useGravity = true;

    //Force = UnityEngine.Random.Range(-20, 20); //获取随机的风力  
    //disk.GetComponent<Rigidbody>().AddForce(new Vector3(Force, 0, 0), ForceMode.Force);
}

接下来,实现飞碟两种运动的组合:

public void EmitDisk()
{
    if (count - leftTime == 2)
    {
        count = leftTime;
        for(int i = 0; i < _diskNum; i++)//第一关,发射一个飞碟每次;第二关发射两个飞碟每次
        {
            GameObject disk = factory.getDisk();//从飞碟工厂得到飞碟
            Debug.Log(disk);
            usedDisk.Add(disk);//飞碟进入场景
            disk.SetActive(true);
            disk.GetComponent<Renderer>().material.color = _color;
            disk.transform.localScale = _scale;

            if (mode == 1)
                iactionmanager.Normalplay(disk);//让普通动作管理者设计轨迹
            else
                iactionmanager.Physicplay(disk);//物理运动管理者设计
            }

        mode = 3 - mode;//更换下一次运动模式
    }
}

public class IActionManager : MonoBehaviour {
    public PhysicActionManager physicaction;
    public CCActionManager ccaction;

    void Awake()
    {
        physicaction = Singleton<PhysicActionManager>.Instance;
        ccaction = Singleton<CCActionManager>.Instance;
    }
    public void Normalplay(GameObject disk)
    {
        ccaction.playDisk(disk);
    }

    public void Physicplay(GameObject disk)
    {
        physicaction.playDisk(disk);
    }
}

  这样,就完成了两种运动的组合。接下来,就是计分。因为两种运动,在其完成之前,我都添加了重力。所以,飞碟都会下落。因此,飞碟落地则会扣分;若未落地被击中,则会得分:

public void RecycleDisk()//检查需不需要回收飞碟
{
    for (int i = 0; i < usedDisk.Count; i++)
    {
        if (usedDisk[i].activeInHierarchy == false )
        {
            if (usedDisk[i].transform.position.y <= -2)
                _judge.failAdisk();//失分
            usedDisk[i].GetComponent<Rigidbody>().useGravity = false;
            factory.free(usedDisk[i]);//让飞碟工厂回收
            usedDisk.Remove(usedDisk[i]);//移除
        }
    }
}

public void shoot()
{
    if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE))
    {
        Debug.Log("shoot");
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
             hit.transform.gameObject.SetActive(false);
             _judge.scoreADisk();//得分
        }
    }
}
//*************************
void Update()
{
    RecycleDisk();
}

  接着,完善细节就可以完成整个游戏了。

附上完整代码

Singleton.cs

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

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{


    protected static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null)
                {
                    Debug.LogError("An instance of" + typeof(T) +
                        "is need in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}

UserInterface.cs

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


public class UserInterface : MonoBehaviour {
    private IUserAction action;
    float width, height;
    // Use this for initialization
    void Awake()
    {
        Debug.Log("UserInterface");
    }
    void Start()
    {
        Debug.Log("UserInterface");
        action = Singleton<SceneController>.Instance as IUserAction;
    }

    float castw(float scale)
    {
        return (Screen.width - width) / scale;
    }

    float casth(float scale)
    {
        return (Screen.height - height) / scale;
    }

    void OnGUI()
    {
        width = Screen.width / 12;
        height = Screen.height / 12;

        //倒计时
        GUI.Label(new Rect(castw(2f) + 20, casth(6f) - 20, 50, 50), Singleton<SceneController>.Instance.leftTime.ToString());

        //分数
        GUI.Button(new Rect(castw(2f) +400, casth(6f) -20, 80, 30),"分数"+ Singleton<Judge>.Instance.getPoint().ToString());

        GUI.Button(new Rect(castw(2f) + 400, casth(6f) -60, 80, 30), "关卡" + Singleton<RoundController>.Instance.getRound().ToString());

        if (Singleton<SceneController>.Instance.state != State.WIN && Singleton<SceneController>.Instance.state != State.LOSE
            && GUI.Button(new Rect(10, 10, 80, 30), Singleton<SceneController>.Instance.countDownTitle))
        {
            if (Singleton<SceneController>.Instance.countDownTitle == "Start")
            {
                Debug.Log(Singleton<SceneController>.Instance.state);
                action.Resume();//用户继续
                Debug.Log(Singleton<SceneController>.Instance.state);
                Singleton<SceneController>.Instance.countDownTitle = "Pause";
                Singleton<SceneController>.Instance.onCountDown = true;
                StartCoroutine(Singleton<SceneController>.Instance.WaitFourSecond());
            }
            else
            {
                Debug.Log(Singleton<SceneController>.Instance.state);
                action.Pause();//用户暂停
                Debug.Log(Singleton<SceneController>.Instance.state);
                Singleton<SceneController>.Instance.countDownTitle = "Start";
                Singleton<SceneController>.Instance.onCountDown = false;
                StopAllCoroutines();
            }
        }

        if (Singleton<SceneController>.Instance.state == State.WIN)//胜利
        {
            StopAllCoroutines();
            if (GUI.Button(new Rect(castw(2f), casth(6f), width+20, height), "Win!"))
            {
                action.Restart();
            }
        }
        else if (Singleton<SceneController>.Instance.state == State.LOSE)//失败
        {
            StopAllCoroutines();
            if (GUI.Button(new Rect(castw(2f), casth(6f), width+20, height), "Lose!"))
            {
                action.Restart();
            }
        }
    }

}

SceneController.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
 * 所有的分数通过调用Judge实现
 * 所有的关卡控制通过RoudController实现
 * 所有的动作通过IActionManger实现
 * SceneControler负责调度,实现加载场景,暂停,重新开始等
 * */
public enum State { WIN, LOSE, PAUSE, CONTINUE, START };
public interface IUserAction
{
    void EmitDisk();//射击
    void Pause();//用户选择暂停游戏
    void Resume();//继续
    void Restart();//重来
}

public class SceneController : MonoBehaviour, IUserAction
{

    public Camera camera;
    public Color _color;
    public Vector3 _scale;
    public int _diskNum;

    public GameObject bullet;

    public static Judge _judge;
    public DiskFactory factory;
    public IActionManager iactionmanager;
    public RoundController roundcontroller;
    public Director director;
    public State state { get; set; }//游戏状态

    //计时器, 每关60秒倒计时
    public int totalTime;
    public int leftTime;
    public bool onCountDown = false;
    public string countDownTitle = "Start";
    public int count;  //用来计数,每4秒自动发射一次飞碟
    public int mode;//用于轮流选择使用普通动作还是物理运动,1是普通,2是物理

    public static List<GameObject> usedDisk;
    void Awake()
    {
        director = Director.getInstance();
        director.setFPS(60);
        state = State.PAUSE;
        factory = DiskFactory.getInstance();
        roundcontroller = Singleton<RoundController>.Instance;
        _judge = Singleton<Judge>.Instance;
        iactionmanager = Singleton<IActionManager>.Instance;
        usedDisk = new List<GameObject>();

        totalTime = 60;
        countDownTitle = "Start";
        leftTime = totalTime;
        count = leftTime;
    }
    void Start()
    {
        Debug.Log("SceneController");
        loadScene();
        mode = 1;

    }

    void Update()
    {
        if(roundcontroller.getRound() == 3)
        {
            state = State.WIN;
        }
        if(leftTime == 0 && _judge.getPoint() < 100)
        {
            state = State.LOSE;
        }
        EmitDisk();
        shoot();
        RecycleDisk();


    }
    //********************飞碟的调度*****************
    //协程 倒计时
    public IEnumerator WaitFourSecond()
    {
        while (leftTime > 0)
        {
            yield return new WaitForSeconds(1f);
            leftTime--;
        }
    }

    //设置飞碟属性
    public void setting(Color color, Vector3 scale, int diskNum)
    {
        _color = color;
        _scale = scale;
        _diskNum = diskNum;
    }
    //飞碟发射
    public void EmitDisk()
    {
        if (count - leftTime == 2)
        {
            count = leftTime;
            for(int i = 0; i < _diskNum; i++)//第一关,发射一个飞碟每次;第二关发射两个飞碟每次
            {
                GameObject disk = factory.getDisk();//从飞碟工厂得到飞碟
                Debug.Log(disk);
                usedDisk.Add(disk);//飞碟进入场景
                disk.SetActive(true);
                disk.GetComponent<Renderer>().material.color = _color;
                disk.transform.localScale = _scale;

                if (mode == 1)
                    iactionmanager.Normalplay(disk);//让普通动作管理者设计轨迹
                else
                    iactionmanager.Physicplay(disk);//物理运动管理者设计
            }

            mode = 3 - mode;//更换下一次运动模式
        }
    }

    //飞碟回收
    //只回收落地飞碟
    //利用事件触发解决
    //在飞碟的脚本里实现具体回收
    public void RecycleDisk()//检查需不需要回收飞碟
    {
        for (int i = 0; i < usedDisk.Count; i++)
        {
            if (usedDisk[i].activeInHierarchy == false )
            {
                if (usedDisk[i].transform.position.y <= -2)
                    _judge.failAdisk();
                usedDisk[i].GetComponent<Rigidbody>().useGravity = false;
                factory.free(usedDisk[i]);//让飞碟工厂回收
                usedDisk.Remove(usedDisk[i]);//移除
            }
        }
    }

    //用户在游戏状态为开始或者继续时,才能左键射击,返回射击的对象
    public void shoot()
    {
        if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE))
        {
            Debug.Log("shoot");
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                    hit.transform.gameObject.SetActive(false);
                    _judge.scoreADisk();
            }
        }
    }
    //********************加载场景*********************
    //加载场景
    public void loadScene()
    {
        Instantiate(Resources.Load("Plane"), new Vector3(0, -4, 0), Quaternion.identity);
    }

    //********************游戏控制****************
    //暂停
     public void Pause()
    {
        state = State.PAUSE;
        for (int i = 0; i < usedDisk.Count; i++)
        {
            usedDisk[i].SetActive(false);//暂停后飞碟不可见;通过DiskFactory实现
        }
    }

    //继续
    public void Resume()
    {
        state = State.CONTINUE;
        for (int i = 0; i < usedDisk.Count; i++)
        {
            usedDisk[i].SetActive(true);//恢复后飞碟可见
        }
    }

    //重新开始
    public void Restart()
    {
        _judge.ResetPoint();//Judge实现,分数清零
        roundcontroller.setRound(0);
        roundcontroller.nextRound();
        state = State.START;
    }
    public void setTime()
    {
        totalTime = 60;
        countDownTitle = "Start";
        leftTime = totalTime;
        count = leftTime;
    }
}

RoundController.cs

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

public class RoundController : MonoBehaviour
{
    public SceneController scene;
    int round;


    // Use this for initialization
    void Start()
    {
        scene = Singleton<SceneController>.Instance;
        round = 0;
    }

    //下一个关卡
    public void nextRound()
    {
        scene.setTime();
        round++;
        loadRound(round);

    }
    //设置关卡
    public void setRound(int round)
    {
        this.round = round;
    }
    //获取关卡
    public int getRound()
    {
        return round;
    }
    //转换关卡
    public void loadRound(int round)
    {
        float r, g, b;
        Color color;
        Vector3 scale;
        float d;//飞碟的直径

        r = Random.Range(0f, 0.1f);
        g = Random.Range(0f, 0.1f);
        b = Random.Range(0f, 0.1f);
        color = new Color(r, g, b);

        d = Random.Range(2.1f, 3.0f);
        scale = new Vector3(d, 0.01f, d);
        switch (round)
        {
            case 1:
                scene.setting(color, scale, 1);
                break;
            case 2:
                scene.setting(color, scale, 2);
                break;
        }
    }
}

Judge.cs

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

public class Judge : MonoBehaviour {
    public int point;
    public int oneDiskPoint;

    private RoundController roundcontroller;
    private SceneController scene;

    void Awake()
    {
        point = 0;
        oneDiskPoint = 10;
        roundcontroller = Singleton<RoundController>.Instance;
    }
    //默认开始第一关
    void Start()
    {
        roundcontroller.nextRound();
    }

    void Update()
    {
        if(point == 100)
        {
            roundcontroller.nextRound();
            point = 0;
        }
    }
    //*************************游戏分数***************
    //击中飞碟得分
    public void scoreADisk()
    {
        point += oneDiskPoint;
    }

    //掉落飞碟失去分数
    public void failAdisk()
    {
        point -= oneDiskPoint;
    }

    //获取分数
    public int getPoint()
    {
        return point;
    }

    //***********************游戏控制辅助函数***************
   public void ResetPoint()
    {
        point = 0;
    }
}

CCActionManager.cs

using System.Collections.Generic;
using UnityEngine;

public interface ISSActionCallback
{
    void actionDone(SSAction source);
}

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 class MoveToAction : SSAction
{
    public Vector3 target;
    public float speed;

    private MoveToAction() { }
    public static MoveToAction getAction(Vector3 target, float speed)
    {
        MoveToAction action = ScriptableObject.CreateInstance<MoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    public override void Update()
    {
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
        if (this.transform.position == target)
        {
            this.destroy = true;
            this.callback.actionDone(this);
        }
    }

    public override void Start() { }

}

public class SequenceAction : SSAction, ISSActionCallback
{
    public List<SSAction> sequence;
    public int repeat = -1; //-1表示无限循环,0表示只执行一遍,repeat> 0 表示重复repeat遍
    public int currentAction = 0;//当前动作列表里,执行到的动作序号

    public static SequenceAction getAction(int repeat, int currentActionIndex, List<SSAction> sequence)
    {
        SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();
        action.sequence = sequence;
        action.repeat = repeat;
        action.currentAction = currentActionIndex;
        return action;
    }

    public override void Update()
    {
        if (sequence.Count == 0) return;
        if (currentAction < sequence.Count)
        {
            sequence[currentAction].Update();
        }
    }

    public void actionDone(SSAction source)
    {
        source.destroy = false;
        this.currentAction++;
        if (this.currentAction >= sequence.Count)
        {
            this.currentAction = 0;
            if (repeat > 0) repeat--;
            if (repeat == 0)
            {
                this.destroy = true;
                this.callback.actionDone(this);
            }
        }
    }

    public override void Start()
    {
        foreach (SSAction action in sequence)
        {
            action.gameObject = this.gameObject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
    }

    void OnDestroy()
    {
        foreach (SSAction action in sequence)
        {
            DestroyObject(action);
        }
    }
}


public class SSActionManager : MonoBehaviour
{
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingToAdd = new List<SSAction>();
    private List<int> watingToDelete = new List<int>();

    protected void Update()
    {
        foreach (SSAction ac in waitingToAdd)
        {
            actions[ac.GetInstanceID()] = ac;
        }
        waitingToAdd.Clear();

        foreach (KeyValuePair<int, SSAction> kv in actions)
        {
            SSAction ac = kv.Value;
            if (ac.destroy)
            {
                watingToDelete.Add(ac.GetInstanceID());
            }
            else if (ac.enable)
            {
                ac.Update();
            }
        }

        foreach (int key in watingToDelete)
        {
            SSAction ac = actions[key];
            actions.Remove(key);
            DestroyObject(ac);
        }
        watingToDelete.Clear();
    }

    public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback whoToNotify)
    {
        action.gameObject = gameObject;
        action.transform = gameObject.transform;
        action.callback = whoToNotify;
        waitingToAdd.Add(action);
        action.Start();
    }

}

public class CCActionManager : SSActionManager, ISSActionCallback
{
    public RoundController scene;
    public MoveToAction action;
    public SequenceAction saction;
    float speed;
    private GameObject temp;

    public void playDisk(GameObject disk)
    {
        temp = disk;
        //随机选取起始坐标
        float x = Random.Range(-1.5f, 1.5f);
        float z = Random.Range(-2f, 2f);
        Vector3 initialPos = new Vector3(x, -3.7f, z);
        disk.transform.position = initialPos;


        //随机选取发射方向
        float xEnd = Random.Range(-2f, 2f);
        float yEnd = Random.Range(6f, 8f);
        float zEnd = Random.Range(0f, 5f);
        Vector3 dir = new Vector3(xEnd, yEnd, zEnd);

        //随机设置速度
        float speed = Random.Range(10f,50f);

        action = MoveToAction.getAction(dir, speed);
        RunAction(disk, action, this);
    }

    protected void Start()
    {
        Debug.Log("CCactionmanger");
    }

    protected new void Update()
    {
        base.Update();
    }

    public void actionDone(SSAction source)
    {
        if(source == action)
        {
            temp.GetComponent<Rigidbody>().useGravity = true;
        }
    }
}

PhysicActionManager.cs

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

public class PhysicActionManager : MonoBehaviour
{

    public RoundController scene;
    //public MoveToAction action1, action2;
    //public SequenceAction saction;
    private float speed = 5.0f;//初速
    //private float Force = 0f; //风力


    public void playDisk(GameObject disk)
    {
        //随机选取起始坐标
        float x = Random.Range(-1.5f, 1.5f);
        float z = Random.Range(0f, 0.5f);
        Vector3 initialPos = new Vector3(x, -3.7f, z);
        disk.transform.position = initialPos;


        //随机选取发射方向
        float xEnd = Random.Range(-2f, 2f);
        float yEnd = Random.Range(6f, 8f);
        float zEnd = Random.Range(0f, 5f);
        Vector3 dir = new Vector3(xEnd, yEnd, zEnd);

        disk.transform.up = dir;
        disk.GetComponent<Rigidbody>().velocity = dir * speed; //设置速度
        disk.GetComponent<Rigidbody>().useGravity = true;
    }
}

IActionManager.cs

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

public class IActionManager : MonoBehaviour {
    public PhysicActionManager physicaction;
    public CCActionManager ccaction;

    void Awake()
    {
        physicaction = Singleton<PhysicActionManager>.Instance;
        ccaction = Singleton<CCActionManager>.Instance;
    }
    public void Normalplay(GameObject disk)
    {
        ccaction.playDisk(disk);
    }

    public void Physicplay(GameObject disk)
    {
        physicaction.playDisk(disk);
    }
}

DiskFactory.cs

using System;
using System.Collections.Generic;
using UnityEngine;
/**
 * 储存和free飞碟 
 * */
public class DiskFactory : System.Object {
    private static DiskFactory _instance;
    private static List<GameObject> unused;//飞碟队列
    private static GameObject gameobject;

    public static DiskFactory getInstance()
    {
        if (_instance == null)
        {
            _instance = new DiskFactory();
            unused = new List<GameObject>();
        }
        return _instance;
    }

    //获取飞碟
    public GameObject getDisk()
    {
        if (unused.Count == 0)
        {
            gameobject = GameObject.Instantiate(Resources.Load("Cylinder"), new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
            gameobject.transform.Rotate(80f, 0, 0);
            return gameobject;
        }
        else
            return unused[0];
    }

    //free飞碟
    public void free(GameObject obj)
    {
        unused.Add(obj);
    }
}

Director.cs

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

public class Director : System.Object
{
    public static Director _instance;
    public IUserAction currentScenceController { get; set; }
    public bool running { get; set; }


    public static Director getInstance()
    {
        if (_instance == null)
        {
            _instance = new Director();
        }
        return _instance;
    }

    public int getFPS()
    {
        return Application.targetFrameRate;
    }

    public void setFPS(int fps)
    {
        Application.targetFrameRate = fps;
    }

  以上就是本次游戏的所有内容,希望对大家有所帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kiloveyousmile

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值