3D游戏编程与设计4——游戏对象与图形基础

1、基本操作演练

  • 下载 Fantasy Skybox FREE,构建自己的游戏场景
    ① 创建自己的天空盒:
    3D游戏编程与设计4——游戏对象与图形基础_第1张图片
    ② 应用到游戏场景中:
    3D游戏编程与设计4——游戏对象与图形基础_第2张图片
    ③ 在场景中加入地形对象,再刷上树,游戏场景便制作完成了:
    3D游戏编程与设计4——游戏对象与图形基础_第3张图片
  • 写一个简单的总结,总结游戏对象的使用

Unity 中常用的游戏对象主要有以下几类:

  • 空对象:是不显示却最常用的游戏对象,可用于放置场景控制脚本,生成该场景所需的游戏对象。
  • 摄像机:用于控制游戏场景的视角与游戏对象的渲染。
  • 天空盒:用于控制游戏界面中的天空背景。
  • 光源:用于调节游戏界面中不同地方的明暗程度。
  • 地形:用于创建陆地、山川、河流、小径等地理结构,打造用户的游戏场景。
  • 音源:用于管理游戏过程中的背景音乐,达到想要的听觉效果。
  • 3D 物体:是游戏中具体的可视化对象,具有一定的立体形态与材质纹理。

每个对象都有 Active、Name、Tag、Layer、Transform 等基本属性,用于控制游戏对象的渲染、样式、位置等等。也可给游戏对象添加脚本,赋予其行为方式,实现 UI 交互。

2、编程实践

  • 牧师与魔鬼 动作分离版
    • 【2019新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

动作分离:可以通过对游戏对象增加可执行的动作,可以划分出很多的基础动作,如上船、下船等,提取基础动作,然后使用类的方法来执行游戏对象的动作,同时通过配备动作管理者,让其调度游戏对象执行动作。

动作管理者 SSActionManager:通过创建类来管理动作的集合,展示了集合对象的使用方法。管理 SequenceAction 和 SSAction,传递游戏对象,控制游戏对象做动作和切换动作。SSActionManager 继承了 ISSActionCallback 接口,通过这个接口,当动作做完时会告诉 SSActionManager,SSActionManager 决定如何执行下一个动作。实现代码如下:

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

public class SSActionManager : MonoBehaviour {

    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

	// Use this for initialization
	protected void Start () {
		
	}
	
	// Update is called once per frame
	protected void Update () {
		foreach(SSAction ac in waitingAdd)
        {
            actions[ac.GetInstanceID()] = ac;
        }
        waitingAdd.Clear();

        foreach(KeyValuePair<int, SSAction> kv in actions)
        {
            SSAction ac = kv.Value;
            if(ac.destroy)
            {
                waitingDelete.Add(ac.GetInstanceID());// release action
                ac.destroy = false;
            }
            else if(ac.enable)
            {
                ac.Update(); // update action
            }
        }

        foreach (int key in waitingDelete)
        {
            SSAction ac = actions[key];
            actions.Remove(key);
            DestroyObject(ac);
            Debug.Log("delete");
        }
        waitingDelete.Clear();
	}

    public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager)
    {
        action.gameobject = gameobject;
        action.transform = gameobject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }
}

组合动作实现 CCSequenceAction:SequenceAction 继承了 ISSActionCallback,因为组合动作是每一个动作的顺序完成,它管理这一连串动作中的每一个小的动作,所以当每个小的动作完成时,也要发消息告诉它,它得到消息后去处理下一个动作。SequenceAction 也继承了 SSAction,因为组合动作需要游戏对象,需要标识是否摧毁,有一个组合动作的管理者的接口。实现代码如下:

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

public class CCSequenceAction : SSAction, ISSActionCallback {

    public List<SSAction> sequence;
    public int repeat = -1;//repeat forever
    public int start = 0;

    public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence)
    {
        CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
        action.repeat = repeat;
        action.sequence = sequence;
        action.start = 0;
        return action;
    }

    // Use this for initialization
    public override void Start () {
		foreach (SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
	}
	
	// Update is called once per frame
	public override void Update () {
        if (sequence.Count == 0) return;
        if(start < sequence.Count)
        {
            sequence[start].Update();
        }
	}

    public void SSActionEvent (SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objectParam = null)
    {
        source.destroy = false;
        this.start++;
        if(this.start >= sequence.Count)
        {
            this.start = 0;
            if (repeat > 0) repeat--;
            if(repeat == 0)
            {
                this.destroy = true;
                this.callback.SSActionEvent(this);
            }
        }
    }

    void OnDestroy()
    {
        //TODO: something
    }
}

移动动作实现 CCMoveToAction:以速度 speed 向目的地 target 移动。实现代码如下:

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

public class CCMoveToAction : SSAction {

    public Vector3 target;

    public static CCMoveToAction GetSSAction(Vector3 target)
    {
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.target = target;
        return action;
    }

	// Use this for initialization
	public override void Start () {
		
    }

    // Update is called once per frame
    public override void Update () {
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, Time.deltaTime * 2);
        if (this.transform.position == target)
        {
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }
}

动作基类 SSAction:继承了 ScriptableObject。实现代码如下:

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

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; }

    protected SSAction() {}

	// Use this for initialization
	public virtual void Start () {
        throw new System.NotImplementedException();
	}
	
	// Update is called once per frame
	public virtual void Update () {
        throw new System.NotImplementedException();
	}
}

SSDirector:移动游戏对象。实现代码如下:

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

public class SSDirector : System.Object
{

    // singlton instance
    private static SSDirector _instance;

    public ISceneController currentSceneController { get; set; }
    public bool running { get; set; }

    //get instance anytime anywhere!
    public static SSDirector getInstance()
    {
        if (_instance == null)
        {
            _instance = new SSDirector();
        }
        return _instance;
    }

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

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

FirstConstroller 实现代码如下:

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

//游戏中提及的事物:牧师、恶魔、船、河、岸
//玩家动作:点击恶魔、点击牧师、点击船、点击重新开始按钮

public class FirstController : MonoBehaviour, ISceneController, devilAction, priestAction, boatAction
{
    public CCActionManager actionManager;
    public bool boat_is_right = true;
    public bool boat_left_empty = true;
    public bool boat_right_empty = true;
    List<bool> right_position_empty = new List<bool>();
    List<bool> left_position_empty = new List<bool>();
   
    public bool boat_move_to_left;
    public bool boat_move_to_right;
    public GameObject boat_to_be_moved = null;
    public GameObject right_on_boat = null;
    public GameObject left_on_boat = null;
    public int left_devil_count;
    public int left_priest_count;
    public int right_devil_count;
    public int right_priest_count;
    public bool game_over;
    public string left_type = null;
    public string right_type = null;
    public GameObject priest1;
    public GameObject priest2;
    public GameObject priest3;
    public GameObject devil1;
    public GameObject devil2;
    public GameObject devil3;
    public GameObject boat;
    public bool win;
    public float time;

    // the first scripts
    void Awake()
    {

        left_devil_count = 0;
        left_priest_count = 0;
        right_priest_count = 3;
        right_devil_count = 3;
        game_over = false;
        int i;
        for (i = 0; i < 6; i++)
        {
            right_position_empty.Add(false);
            left_position_empty.Add(true);
            //right_position_empty[i] = false;
            //left_position_empty[i] = true;
        }
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentSceneController = this;
        director.currentSceneController.LoadResources();
        boat_move_to_right = false;
        boat_move_to_left = false;
        win = false;
        Debug.Log("left_priest:" + left_priest_count.ToString());
        Debug.Log("left_devil:" + left_devil_count.ToString());
        Debug.Log("right_priest:" + right_priest_count.ToString());
        Debug.Log("right_devil:" + right_devil_count.ToString());
    }

    // loading resources for first scene
    public void LoadResources()
    {
        GameObject left_land = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/land"));
        left_land.name = "left_land";
        left_land.transform.position = new Vector3(-left_land.transform.position.x, left_land.transform.position.y, left_land.transform.position.z);
        GameObject right_land = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/land"));
        left_land.name = "right_land";

        GameObject river = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/river"));
        river.name = "river";

        priest1 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/priest"));
        priest1.name = "priest1";
        priest1.transform.position = new Vector3(6, priest1.transform.position.y, priest1.transform.position.z);
        priest2 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/priest"));
        priest2.name = "priest2";
        priest2.transform.position = new Vector3(7, priest2.transform.position.y, priest2.transform.position.z);
        priest3 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/priest"));
        priest3.name = "priest3";
        priest3.transform.position = new Vector3(8, priest3.transform.position.y, priest3.transform.position.z);

        devil1 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/devil"));
        devil1.name = "devil1";
        devil1.transform.position = new Vector3(9, devil1.transform.position.y, devil1.transform.position.z);
        devil2 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/devil"));
        devil2.name = "devil2";
        devil2.transform.position = new Vector3(10, devil2.transform.position.y, devil2.transform.position.z);
        devil3 = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/devil"));
        devil3.name = "devil3";
        devil3.transform.position = new Vector3(11, devil3.transform.position.y, devil3.transform.position.z);

        boat = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/boat"));
        boat.name = "boat";
    }

    public void Restart()
    {
        boat_is_right = true;
        boat_left_empty = true;
        boat_right_empty = true;
        int i;
        for (i = 0; i < 6; i++)
        {
            right_position_empty[i] = false;
            left_position_empty[i] = true;
        }
        boat_move_to_right = false;
        boat_move_to_left = false;
        boat_to_be_moved = null;
        right_on_boat = null;
        left_on_boat = null;
        left_devil_count = 0;
        left_priest_count = 0;
        right_devil_count = 3;
        right_priest_count = 3;
        game_over = false;
        left_type = null;
        right_type = null;
        priest1.transform.position = new Vector3(6, 2.5f, 0);
        priest2.transform.position = new Vector3(7, 2.5f, 0);
        priest3.transform.position = new Vector3(8, 2.5f, 0);
        devil1.transform.position = new Vector3(9, 2.5f, 0);
        devil2.transform.position = new Vector3(10, 2.5f, 0);
        devil3.transform.position = new Vector3(11, 2.5f, 0);
        boat.transform.position = new Vector3(3, 0, 0);
        win = false;
        time = 60;
        StartCoroutine(Timer());
    }

    public void devil_move(GameObject obj)
    {
        if (boat_move_to_left || boat_move_to_right || game_over || win)
        {
            return;
        }
        if (boat_is_right)
        {
            if (obj.transform.position.x >= 6)
            {
                if (boat_left_empty)
                {
                    //Debug.Log((int)obj.transform.position.x - 6);
                    right_position_empty[(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(2, 2, 0);
                    boat_left_empty = false;
                    left_on_boat = obj;
                    left_type = "devil";

                }
                else if (boat_right_empty)
                {
                    right_position_empty[(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(4, 2, 0);
                    boat_right_empty = false;
                    right_on_boat = obj;
                    right_type = "devil";
                }
            }
            else if (obj.transform.position.x >= 2 && obj.transform.position.x <= 4)
            {
                if (obj.transform.position.x == 2)
                {
                    boat_left_empty = true;
                    left_on_boat = null;
                    left_type = null;
                }
                else
                {
                    boat_right_empty = true;
                    right_on_boat = null;
                    right_type = null;
                }
                int i;
                for (i = 0; i < 6; i++)
                {
                    if (right_position_empty[i])
                    {
                        obj.transform.position = new Vector3(i + 6, 2.5f, 0);
                        right_position_empty[i] = false;
                        break;
                    }
                }

            }
        }
        else
        {
            if (obj.transform.position.x <= -6)
            {
                if (boat_left_empty)
                {
                    left_position_empty[-(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(-4, 2, 0);
                    boat_left_empty = false;
                    left_on_boat = obj;
                    left_type = "devil";

                }
                else if (boat_right_empty)
                {
                    left_position_empty[-(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(-2, 2, 0);
                    boat_right_empty = false;
                    right_on_boat = obj;
                    right_type = "devil";
                }
            }
            else if (obj.transform.position.x >= -4 && obj.transform.position.x <= -2)
            {
                if (obj.transform.position.x == -4)
                {
                    boat_left_empty = true;
                    left_on_boat = null;
                    left_type = null;
                }
                else
                {
                    boat_right_empty = true;
                    right_on_boat = null;
                    right_type = null;
                }
                int i;
                for (i = 0; i < 6; i++)
                {
                    if (left_position_empty[i])
                    {
                        //Debug.Log((int)obj.transform.position.x - 6);
                        obj.transform.position = new Vector3(-i - 6, 2.5f, 0);
                        left_position_empty[i] = false;
                        break;
                    }
                }

            }
        }
    }

    public void priest_move(GameObject obj)
    {
        if (boat_move_to_left || boat_move_to_right || game_over || win)
        {
            return;
        }
        if (boat_is_right)
        {
            if (obj.transform.position.x >= 6)
            {
                if (boat_left_empty)
                {
                    Debug.Log((int)obj.transform.position.x - 6);
                    right_position_empty[(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(2, 2, 0);
                    boat_left_empty = false;
                    left_on_boat = obj;
                    left_type = "priest";

                }
                else if (boat_right_empty)
                {
                    right_position_empty[(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(4, 2, 0);
                    boat_right_empty = false;
                    right_on_boat = obj;
                    right_type = "priest";
                }
            }
            else if (obj.transform.position.x >= 2 && obj.transform.position.x <= 4)
            {
                if (obj.transform.position.x == 2)
                {
                    boat_left_empty = true;
                    left_on_boat = null;
                    left_type = null;
                }
                else
                {
                    boat_right_empty = true;
                    right_on_boat = null;
                    right_type = null;
                }
                int i;
                for (i = 0; i < 6; i++)
                {
                    if (right_position_empty[i])
                    {
                        obj.transform.position = new Vector3(i + 6, 2.5f, 0);
                        right_position_empty[i] = false;
                        break;
                    }
                }

            }
        }
        else
        {
            if (obj.transform.position.x <= -6)
            {
                if (boat_left_empty)
                {
                    left_position_empty[-(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(-4, 2, 0);
                    boat_left_empty = false;
                    left_on_boat = obj;
                    left_type = "priest";

                }
                else if (boat_right_empty)
                {
                    left_position_empty[-(int)obj.transform.position.x - 6] = true;
                    obj.transform.position = new Vector3(-2, 2, 0);
                    boat_right_empty = false;
                    right_on_boat = obj;
                    right_type = "priest";
                }
            }
            else if (obj.transform.position.x >= -4 && obj.transform.position.x <= -2)
            {
                if (obj.transform.position.x == -4)
                {
                    boat_left_empty = true;
                    left_on_boat = null;
                    left_type = null;
                }
                else
                {
                    boat_right_empty = true;
                    right_on_boat = null;
                    right_type = null;
                }
                int i;
                for (i = 0; i < 6; i++)
                {
                    if (left_position_empty[i])
                    {
                        obj.transform.position = new Vector3(-i - 6, 2.5f, 0);
                        left_position_empty[i] = false;
                        break;
                    }
                }

            }
        }
    }

    public void boat_move(GameObject boat)
    {
        if (boat_move_to_left || boat_move_to_right || game_over || win)
        {
            Debug.Log("error!");
            return;
        }
        if (left_type != null || right_type != null)
        {
            if (boat_is_right)
            {
                //boat_to_be_moved = boat;
                boat_move_to_left = true;
                actionManager.RunAction(boat, actionManager.boatToLeft,actionManager);
                Debug.Log("toLeft");
                if (left_on_boat != null)
                    actionManager.RunAction(this.left_on_boat, actionManager.leftToLeft, actionManager);
                if (right_on_boat != null)
                    actionManager.RunAction(right_on_boat, actionManager.rightToLeft, actionManager);
                boat_is_right = false;
                if (left_type == "devil")
                {
                    left_devil_count++;
                    right_devil_count--;
                }
                else if (left_type == "priest")
                {
                    left_priest_count++;
                    right_priest_count--;
                }
                if (right_type == "devil")
                {
                    left_devil_count++;
                    right_devil_count--;
                }
                else if (right_type == "priest")
                {
                    left_priest_count++;
                    right_priest_count--;
                }
            }
            else
            {
                //boat_to_be_moved = boat;
                boat_move_to_right = true;
                actionManager.RunAction(boat, actionManager.boatToRight, actionManager);
                if (left_on_boat != null)
                    actionManager.RunAction(this.left_on_boat, actionManager.leftToRight, actionManager);
                if (right_on_boat != null)
                    actionManager.RunAction(right_on_boat, actionManager.rightToRight, actionManager);
                boat_is_right = true;
                if (left_type == "devil")
                {
                    left_devil_count--;
                    right_devil_count++;
                }
                else if (left_type == "priest")
                {
                    left_priest_count--;
                    right_priest_count++;
                }
                if (right_type == "devil")
                {
                    left_devil_count--;
                    right_devil_count++;
                }
                else if (right_type == "priest")
                {
                    left_priest_count--;
                    right_priest_count++;
                }
            }
        }
        Debug.Log("left_priest:" + left_priest_count.ToString());
        Debug.Log("left_devil:" + left_devil_count.ToString());
        Debug.Log("right_priest:" + right_priest_count.ToString());
        Debug.Log("right_devil:" + right_devil_count.ToString());
    }

    public void game_over_judge()
    {
        if ((right_devil_count > right_priest_count && right_priest_count != 0) || (left_devil_count > left_priest_count && left_priest_count != 0))
        {
            game_over = true;
            time = 0;
        }
        else if (left_priest_count == 3 && left_devil_count == 3)
        {
            win = true;
            time = 0;
        }
    }

    // Use this for initialization
    void Start()
    {
        time = 60;
        StartCoroutine(Timer());
    }

    public IEnumerator Timer()
    {
        while (time > 0)
        {
            yield return new WaitForSeconds(1);
            print(time);
            time--;
        }
        game_over = true;
    }

    // Update is called once per frame
    void Update()
    {
        /*if (boat_move_to_left && boat_to_be_moved != null)
        {
            this.boat_to_be_moved.transform.position = Vector3.MoveTowards(this.boat_to_be_moved.transform.position, new Vector3(-3, 0, 0), Time.deltaTime * 2);
            if (left_on_boat != null)
                this.left_on_boat.transform.position = Vector3.MoveTowards(this.left_on_boat.transform.position, new Vector3(-4, 2, 0), Time.deltaTime * 2);
            if (right_on_boat != null)
                this.right_on_boat.transform.position = Vector3.MoveTowards(this.right_on_boat.transform.position, new Vector3(-2, 2, 0), Time.deltaTime * 2);
            if (this.boat_to_be_moved.transform.position == new Vector3(-3, 0, 0))
            {
                boat_move_to_left = false;
                game_over_judge();
            }
        }
        if (boat_move_to_right && boat_to_be_moved != null)
        {
            this.boat_to_be_moved.transform.position = Vector3.MoveTowards(this.boat_to_be_moved.transform.position, new Vector3(3, 0, 0), Time.deltaTime * 2);
            if (left_on_boat != null)
                this.left_on_boat.transform.position = Vector3.MoveTowards(this.left_on_boat.transform.position, new Vector3(2, 2, 0), Time.deltaTime * 2);
            if (right_on_boat != null)
                this.right_on_boat.transform.position = Vector3.MoveTowards(this.right_on_boat.transform.position, new Vector3(4, 2, 0), Time.deltaTime * 2);
            if (this.boat_to_be_moved.transform.position == new Vector3(3, 0, 0))
            {
                boat_move_to_right = false;
                game_over_judge();
            }
        }*/

    }

    void OnGUI()
    {

        GUI.Box(new Rect(Screen.width / 2 - 75, 10, 150, 40), "Left Time: \n" + time.ToString());

        if (game_over)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), game_over_window, "Game Over!");
        }
        if (win)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), win_window, "You Win!");
        }
    }

    void game_over_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Restart"))
        {
            Debug.Log("Restart");

            Restart();
        }

    }

    void win_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Restart"))
        {
            Debug.Log("Restart");

            Restart();
        }
    }
}

你可能感兴趣的:(3D游戏编程与设计)