本项目利用Unity的物理引擎,实现了简单的打飞碟游戏,UML图如上。
项目的代码共有五个文件,其中Controller负责管理整个场景以及每一轮的游戏,DiskFactory负责飞碟的生产和回收,Disk存储飞碟数据,BasePoint定义了每种颜色飞碟的基础分,其成员变量ColorPoint可以在Inspector中修改。
本项目实现了每一轮的飞碟数量、大小、各种颜色的出现频率、速度、发射角度都不尽相同,且每一轮飞碟的分数也有所增加。
下面是每一个文件的完整代码和相关解释:
BasePoint.cs
using UnityEngine;
[CreateAssetMenu(fileName = "BasePoint", menuName = "Custom/BasePoint")]
public class BasePoint : ScriptableObject
{
public enum DiskColor
{
Orange,
Red,
DeepBlue,
Black
}
void OnEnable()
{
if (colorPoints == null || colorPoints.Length == 0)
{
colorPoints = new ColorPoint[4];
colorPoints[0].color = DiskColor.Orange;
colorPoints[1].color = DiskColor.Red;
colorPoints[2].color = DiskColor.DeepBlue;
colorPoints[3].color = DiskColor.Black;
colorPoints[0].baseColor = new Color(1.0f, 0.5f, 0.0f);
colorPoints[1].baseColor = Color.red;
colorPoints[2].baseColor = new Color(0.0f, 0.0f, 0.5f);
colorPoints[3].baseColor = Color.black;
}
}
[System.Serializable]
public struct ColorPoint
{
public DiskColor color;
public Color baseColor;
public int baseScore;
}
[SerializeField]
public ColorPoint[] colorPoints;
}
这段代码定义了一个名为BasePoint
的ScriptableObject
。ScriptableObject
是Unity中的一个特殊类,用于持久化一些数据,例如配置或设置。在这个例子中,BasePoint
就像一个数据表,它存储了关于不同颜色飞碟的一些信息。
首先,这段代码定义了一个名为DiskColor
的枚举类型,用于表示飞碟可能的颜色:Orange, Red, DeepBlue, 和 Black。
然后,在OnEnable
方法中,如果colorPoints
数组为空或长度为0,会为其分配空间并初始化。每个ColorPoint
结构体表示一种颜色的飞碟,包括飞碟的颜色、基础颜色值以及基础分数。注意,这段代码中并没有为ColorPoint
的baseScore
赋值,这是在Inspector中完成的。
总结起来,这个BasePoint
类就是一个飞碟颜色与其对应属性(包括基础颜色值和基础分数)的映射。这样,你可以在游戏中方便地根据飞碟的颜色获取其相关信息。
Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Controller : MonoBehaviour
{
DiskFactory factory;
public BasePoint basePoint;
List<Disk> LDisk;
public static int score;
int round;
public static bool gameover;
// the first scripts
void Awake()
{
factory = DiskFactory.Instance;
factory.Initialize();
LDisk = new List<Disk>(); // 创建一个空的列表来存储 Disk
}
public void Pause()
{
throw new System.NotImplementedException();
}
public void Resume()
{
throw new System.NotImplementedException();
}
// Use this for initialization
void Start()
{
;
}
// Update is called once per frame
void Update()
{ if(gameover)
{ return;
}
if(LDisk.Count == 0)
{
//give advice first
round += 1;
Debug.Log("Round " + round);
Ruler r = new Ruler(round);
int N = r.num();
for (int i = 0; i < N; i++)
{
Disk d = factory.GetDisk(r);
LDisk.Add(d); // 将得到的 Disk 添加到列表中
for (int j = 0; j < 4; j++)
{
Debug.Log(d.color);
if (d.color == basePoint.colorPoints[j].baseColor)
{
Rigidbody rb = d.GetComponent<Rigidbody>();
Debug.Log("x:" + d.transform.localScale.x);
float score = basePoint.colorPoints[j].baseScore + rb.velocity.magnitude + (2 - d.transform.localScale.x) * 10;
Debug.Log("Score: " + (int)score);
d.setscore((int)score);
}
}
}
}
//give advice first
for (int i = LDisk.Count - 1; i >= 0; i--)
{
Disk disk = LDisk[i];
if (!disk.gameObject.activeSelf)
{
if (disk.goal)
{
score += disk.score; // 将得分加到静态变量 score 上
Debug.Log("当前得分:" + score);
}
LDisk.RemoveAt(i); // 从列表中移除该 Disk
}
}
if(LDisk.Count == 0 && round == 6)
{
gameover = true;
}
}
}
这段代码定义了一个名为`Controller`的MonoBehaviour类,它是你的飞碟游戏的主要控制器。这个类在每一轮中管理和追踪飞碟的生成和得分,还决定何时结束游戏。
Awake() 方法:
在`Awake`方法中,控制器初始化了`DiskFactory`实例和一个用来存储`Disk`的列表。
Start() 方法:
在`Start`方法中,没有执行任何代码。
Update() 方法:
`Update`方法是每帧都会调用的方法,用于更新游戏的状态。
1. 首先,如果游戏已经结束(`gameover`为true),则直接返回,不执行任何操作。
2. 如果当前没有活动的飞碟(`LDisk.Count`为0),则进入下一轮,生成一定数量的飞碟,数量由`Ruler`类计算得出。每个飞碟的得分是基于它的颜色、速度和大小的。
3. 接着,对于每一个活动的飞碟,如果它不再活动(可能是飞出屏幕或被击中),则根据是否命中目标来更新总得分,并从活动飞碟的列表中移除。
4. 如果所有轮次都结束并且没有活动的飞碟,游戏结束。
Pause() 和 Resume() 方法:
这两个方法目前还没有实现,但从名字来看,它们应该用于暂停和恢复游戏。
总的来说,这个`Controller`类负责游戏的主要流程,包括飞碟的生成和移除,得分的计算,以及决定何时结束游戏。
Disk.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Disk : MonoBehaviour
{
public float scale;
public Color color;
public int score;
public bool goal;
public void Initialize(float scale, Color color)
{
this.scale = scale;
this.color = color;
score = 0;
float newscale = scale * 2;
Debug.Log("newscale:" + newscale);
transform.localScale = new Vector3(newscale, 0.05f, newscale);
Renderer renderer = GetComponent<Renderer>();
renderer.material.color = color;
}
public void setscore(int s)
{
score = s;
}
void Start()
{
;
}
// 当对象被鼠标点击时的处理
void OnMouseDown()
{
goal = true;
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
// 当对象发生碰撞时的处理
void OnCollisionEnter(Collision collision)
{
// 如果碰撞的对象是地面
if (collision.gameObject.tag == "ground")
{
// 启动协程,延迟一段时间后再将对象设为非活动状态
StartCoroutine(DisableAfterSeconds(1.0f));
}
}
// 协程,延迟一段时间后将对象设为非活动状态
IEnumerator DisableAfterSeconds(float seconds)
{
// 等待指定的秒数
yield return new WaitForSeconds(seconds);
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
private void Update()
{
if (transform.position.y < -10) this.gameObject.SetActive(false);
}
}
这个`Disk`类是一个特定的Unity物体,代表在你的飞碟游戏中的飞碟。
Initialize() 方法:
`Initialize`方法用于设置飞碟的大小和颜色。它接受scale和color作为参数,然后将这些值设置到飞碟的transform和renderer组件上。该方法还将飞碟的分数重置为0。
setscore() 方法:
`setscore`方法接受一个分数作为参数,并将这个分数赋值给飞碟的score。
OnMouseDown() 方法:
`OnMouseDown`方法会在鼠标点击该飞碟时被调用。在被点击时,飞碟会被标记为goal(被击中),并立即被设置为非活动状态(隐藏)。
OnCollisionEnter() 方法:
`OnCollisionEnter`方法在飞碟碰撞到其他物体时被调用。如果飞碟碰撞到的对象标签为"ground",那么它会启动一个协程,在1秒后将飞碟设置为非活动状态。
DisableAfterSeconds() 方法:
`DisableAfterSeconds`是一个协程,它会等待传入的秒数,然后将飞碟设置为非活动状态。
Update() 方法:
在`Update`方法中,如果飞碟的位置低于-10(即飞离了游戏场地),它会立即被设置为非活动状态。
综上,`Disk`类主要负责飞碟的初始化、状态改变(包括被击中和落地后的处理)以及分数设置。
DiskFactory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Disk : MonoBehaviour
{
public float scale;
public Color color;
public int score;
public bool goal;
public void Initialize(float scale, Color color)
{
this.scale = scale;
this.color = color;
score = 0;
float newscale = scale * 2;
Debug.Log("newscale:" + newscale);
transform.localScale = new Vector3(newscale, 0.05f, newscale);
Renderer renderer = GetComponent<Renderer>();
renderer.material.color = color;
}
public void setscore(int s)
{
score = s;
}
void Start()
{
;
}
// 当对象被鼠标点击时的处理
void OnMouseDown()
{
goal = true;
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
// 当对象发生碰撞时的处理
void OnCollisionEnter(Collision collision)
{
// 如果碰撞的对象是地面
if (collision.gameObject.tag == "ground")
{
// 启动协程,延迟一段时间后再将对象设为非活动状态
StartCoroutine(DisableAfterSeconds(1.0f));
}
}
// 协程,延迟一段时间后将对象设为非活动状态
IEnumerator DisableAfterSeconds(float seconds)
{
// 等待指定的秒数
yield return new WaitForSeconds(seconds);
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
private void Update()
{
if (transform.position.y < -10) this.gameObject.SetActive(false);
}
}
这段代码定义了一个名为DiskFactory
的类,它是一个单例工厂类,用于在游戏中创建、回收和管理飞碟(Disk
)对象。
Initialize() 方法:Initialize
方法用于初始化freeDisk
和usedDisk
列表,分别用于存储未使用和正在使用的飞碟对象。
FreeDisk() 方法:FreeDisk
方法会检查所有正在使用的飞碟,如果有飞碟不再活动(可能是飞出屏幕或被击中),则将其回收到未使用的飞碟列表中。
Instance 属性:DiskFactory
类使用了单例模式,Instance
属性用于获取这个类的唯一实例。
GetRandomLocation() 方法:GetRandomLocation
方法会生成一个随机的飞碟出现位置。
GetRandomColor() 和 GetColorByIndex() 方法:
这两个方法用于生成一个随机颜色,根据Ruler
对象中的概率数组决定颜色的生成概率。
GetDisk() 方法:GetDisk
方法是工厂的主要方法,用于生成一个新的飞碟。它首先会尝试回收所有不活动的飞碟,然后生成一个新的飞碟。如果有未使用的飞碟,就直接取出一个进行初始化;如果没有,就创建一个新的飞碟。飞碟的位置、颜色、形状和初速度都是随机生成的,具体的生成规则由传入的Ruler
对象决定。
总的来说,DiskFactory
类的主要责任是飞碟对象的管理和生成,包括对象的回收和重用,以及新对象的创建。通过这种方式,可以有效地管理游戏中的飞碟对象,避免了频繁的对象创建和销毁,优化了内存和性能。
Ruler.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Disk : MonoBehaviour
{
public float scale;
public Color color;
public int score;
public bool goal;
public void Initialize(float scale, Color color)
{
this.scale = scale;
this.color = color;
score = 0;
float newscale = scale * 2;
Debug.Log("newscale:" + newscale);
transform.localScale = new Vector3(newscale, 0.05f, newscale);
Renderer renderer = GetComponent<Renderer>();
renderer.material.color = color;
}
public void setscore(int s)
{
score = s;
}
void Start()
{
;
}
// 当对象被鼠标点击时的处理
void OnMouseDown()
{
goal = true;
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
// 当对象发生碰撞时的处理
void OnCollisionEnter(Collision collision)
{
// 如果碰撞的对象是地面
if (collision.gameObject.tag == "ground")
{
// 启动协程,延迟一段时间后再将对象设为非活动状态
StartCoroutine(DisableAfterSeconds(1.0f));
}
}
// 协程,延迟一段时间后将对象设为非活动状态
IEnumerator DisableAfterSeconds(float seconds)
{
// 等待指定的秒数
yield return new WaitForSeconds(seconds);
// 将对象设为非活动状态
this.gameObject.SetActive(false);
}
private void Update()
{
if (transform.position.y < -10) this.gameObject.SetActive(false);
}
}
Ruler
类用于控制每一轮飞碟的相关参数。这些参数包括飞碟的速度范围、发射角度范围、飞碟大小的缩放因子、飞碟的颜色概率,以及当前轮次。
Ruler() 构造函数:
根据当前的游戏轮次round
来初始化飞碟的参数。速度v_min
和v_max
会随着轮次增加而增加,使得游戏难度逐渐提升。飞碟大小的缩放因子scale
会随着轮次增加而减小,也就是飞碟会变得越来越小。飞碟颜色概率数组probability
会随着轮次的增加而调整,颜色的选择变得越来越多。
num() 方法:num
方法用于随机生成一个数值,这个数值的范围在1到5 + round
之间。这个方法可能用于决定每轮生成的飞碟数量。
总的来说,Ruler
类是一个飞碟参数生成器,它会根据当前的游戏轮次来调整飞碟的各种参数,以此来控制游戏的难度。
UserGUI.cs
using UnityEngine;
public class UserGUI : MonoBehaviour
{
public GUIStyle textStyle; // 自定义的GUIStyle,用于设置字体颜色
void OnGUI()
{
textStyle.normal.textColor = Color.black; // 将字体颜色设置为黑色
GUI.Label(new Rect(10, 10, 200, 20), "Score: " + Controller.score, textStyle);
if (Controller.gameover)
{
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 10, 100, 20), "Game Over", textStyle);
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 + 10, 100, 20), "Final Score: " + Controller.score, textStyle);
}
}
}
该类实现了提示玩家当前得分,以及显示游戏结束时的总得分的功能。