WillKen's Blog.

3D游戏编程与设计-与游戏世界交互

Word count: 1.2kReading time: 6 min
2019/10/09 Share

与游戏世界交互

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

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

如果你的使用工厂有疑问,参考:弹药和敌人:减少,重用和再利用

项目地址

  • FirstController 游戏初始化操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Base;

public class FirstController : MonoBehaviour
{
public UFOfactory factory;
public director director;
private GameObject myUFOfactory;
public GameObject _camera;

void Awake()
{
Random.InitState((int)System.DateTime.Now.Ticks);
myUFOfactory = new GameObject("UFOfactory");
myUFOfactory.AddComponent<UFOfactory>();
director = director.getInstance();
factory = Singleton<UFOfactory>.Instance;
director.currentController = this;
}

private void Start()
{
director = director.getInstance();
}
void Update()
{
if (Input.GetButtonDown("Fire1"))
{

Vector3 mp = Input.mousePosition;
Camera c;
if (_camera != null) c = _camera.GetComponent<Camera>();
else c = Camera.main;
Ray ray = c.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
director.currentController.factory.hitted(hit.transform.gameObject);
}
}
}
}
  • UFOfactory 使用带缓存的工厂模式管理不同飞碟的生产与回收。计分效果(白色1分,灰色2分,黑色3分)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Base;

public class UFOfactory : MonoBehaviour
{
public List<GameObject> used;
public List<GameObject> notUsed;
public List<actions> actions;
public int round = 0;
public int score = 0;

private void Start()
{
used = new List<GameObject>();
notUsed = new List<GameObject>();
actions = new List<actions>();
for(int i = 0; i < 10; i++)
{
notUsed.Add(Object.Instantiate(Resources.Load("Prefabs/UFO1", typeof(GameObject)), new Vector3(0, -20, 0), Quaternion.identity, null) as GameObject);
//Debug.Log ("prefabes");
actions.Add(ScriptableObject.CreateInstance<actions>());
}
for(int i = 0; i < 10; i++)
{
actions[i].ufo = notUsed[i];
}
}

private void Update()
{
if (round <= 10) {
for (int i = 0; i < 10; i++) {
actions [i].Update ();
}
if (notUsed.Count == 10) {
round += 1;
if (round <= 10)
newRound (round);
}
} else {
GUIStyle style1 = new GUIStyle();
style1.normal.background = null;
style1.normal.background = null;
style1.normal.textColor = Color.red;
style1.fontSize = 80;
GUI.Label(new Rect(Screen.width*0.5f, Screen.height*0.5f, 300, 300), "游戏结束!");
}
}
public void hitted(GameObject g)
{
Debug.Log (g.tag);
if (g.gameObject.GetComponent<MeshRenderer>().material.color==Color.white) {
Debug.Log ("1");
score += 1;
} else if (g.gameObject.GetComponent<MeshRenderer>().material.color==Color.gray) {
Debug.Log ("2");
score += 2;
} else if (g.gameObject.GetComponent<MeshRenderer>().material.color==Color.black) {
Debug.Log ("3");
score += 3;
}
this.used.Remove(g);
g.transform.position = new Vector3(0, -20, 0);
for(int i = 0; i < 10; i++)
{
if (actions[i].ufo == g)
actions[i].running = false;
}
this.notUsed.Add(g);
}
public void miss(GameObject g)
{
this.used.Remove(g);
g.transform.position = new Vector3(0, -20, 0);
for (int i = 0; i < 10; i++)
{
if (actions[i].ufo == g)
actions[i].running = false;
}
this.notUsed.Add(g);
}

public void newRound(int round)
{
for(int i = 0; i < 10; i++)
{
used.Add(notUsed[0]);
notUsed.Remove(notUsed[0]);
actions[i].speed = round + 2;
actions[i].Start();
actions[i].running = true;
}
}
}
  • Base定义了Director 和Actions(主要有游戏对象外观设定白色、灰色、黑色,难度设定)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
namespace Base{

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

//Director
public class director : System.Object
{
private static director _instance;
public FirstController currentController { get; set; }
public static director getInstance()
{
if (_instance == null)
{
_instance = new director();
}
return _instance;
}
}

//Actions
public class actions : ScriptableObject
{
public director director;
public GameObject ufo;
Vector3 start;
Vector3 end;
public int speed=5;
public bool running = true;
public int recordType;

public void Start()
{
director = director.getInstance();
start = new Vector3(Random.Range(-6,6), Random.Range(-6,6), 0);
if (start.x < 10 && start.x > -10)
start.x *= 5;
if (start.y < 10 && start.y > -10)
start.y *= 5;
end = new Vector3(-start.x, -start.y, 0);
ufo.transform.position = start;
foreach (Transform child in ufo.transform)
{
child.gameObject.GetComponent<MeshRenderer>().material=Material.Instantiate(Resources.Load("Prefabs/body", typeof(Material))) as Material;
}
int typeOfUFO = Random.Range(1, 4);
recordType = typeOfUFO;
switch (typeOfUFO)
{
case 1:
ufo.tag="easy";
//ufo.GetComponent<MeshRenderer>().material =Material.Instantiate(Resources.Load("Prefabs/easy", typeof(Material))) as Material;
ufo.GetComponent<MeshRenderer>().material.color=Color.white;
break;
case 2:
ufo.tag="middle";
//ufo.GetComponent<MeshRenderer>().material = Material.Instantiate(Resources.Load("Prefabs/middle", typeof(Material))) as Material;
ufo.GetComponent<MeshRenderer>().material.color=Color.gray;
break;
case 3:
//ufo.tag="tough";
//ufo.GetComponent<MeshRenderer>().material = Material.Instantiate(Resources.Load("Prefabs/tough", typeof(Material))) as Material;
ufo.GetComponent<MeshRenderer>().material.color=Color.black;
break;
default:
break;
}
}

public void Update()
{
if (running)
{
ufo.transform.position = Vector3.MoveTowards(ufo.transform.position, end, speed * Time.deltaTime);
if (ufo.transform.position == end)
{
this.director.currentController.factory.miss(this.ufo);
}
}
}

}

}
  • Singleton 场景单实例模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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("No instance of " + typeof(T));
}
}
return instance;
}
}
}

游戏界面

编写一个简单的自定义 Component (选做

CATALOG
  1. 1. 与游戏世界交互
    1. 1.1. 编写一个简单的鼠标打飞碟(Hit UFO)游戏
    2. 1.2. 编写一个简单的自定义 Component (选做)