Unity游戏制作(四)

Unity游戏制作(四)

实验内容

1 .编程实践:编写一个简单的鼠标打飞碟(Hit UFO)游戏

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

实验环境

  • Unity 2020.3.18
  • Windows

技术日记

一、游戏交互与创新

1. Unity 输入处理
1.1 JoyStick – 虚拟轴与按键
public class Joystick : MonoBehaviour {

	public float speedX = 10.0F;
	public float speedY = 10.0F;

	// Update is called once per frame
	void Update () {
		float translationY = Input.GetAxis("Vertical") * speedY;
		float translationX = Input.GetAxis("Horizontal") * speedX;
		translationY *= Time.deltaTime;
		translationX *= Time.deltaTime;
		//transform.Translate(0, translationY, 0);
		//transform.Translate(translationX, 0, 0);
		transform.Translate(translationX, translationY, 0);

		if (Input.GetButtonDown ("Fire1")) {
			Debug.Log ("Fired Pressed");
		}
	}
}
1.2 光标拾取物体程序
public class PickupObject : MonoBehaviour {

	public GameObject cam;

	// Update is called once per frame
	void Update () {
		if (Input.GetButtonDown("Fire1")) {
			Debug.Log ("Fired Pressed");
			Debug.Log (Input.mousePosition);

			Vector3 mp = Input.mousePosition; //get Screen Position

			//create ray, origin is camera, and direction to mousepoint
			Camera ca;
			if (cam != null ) ca = cam.GetComponent<Camera> (); 
			else ca = Camera.main;

			Ray ray = ca.ScreenPointToRay(Input.mousePosition);

			//Return the ray's hit
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit)) {
				print (hit.transform.gameObject.name);
				if (hit.collider.gameObject.tag.Contains("Finish")) { //plane tag
					Debug.Log ("hit " + hit.collider.gameObject.name +"!" ); 
				}
				Destroy (hit.transform.gameObject);
			}
		}
	}
}

程序要点:

  • mousePosition 是 Vector3 ,请不要修改 z 坐标
  • 获取摄像机的 Camera 部件,构建 Ray
  • Camera 部件支持正确生成世界坐标的射线
  • Raycast 函数使用了变参(值参与变参),为什么 hit 必须用变参?
  • 为了优化性能,Raycast 支持在特定层扫描对象
1.3 光标拾取多个物体程序
public class PickupMultiObjects : MonoBehaviour {

	public GameObject cam;

	// Update is called once per frame
	void Update () {
		if (Input.GetButtonDown("Fire1")) {
			Debug.Log ("Fired Pressed");
			Debug.Log (Input.mousePosition);

			Vector3 mp = Input.mousePosition; //get Screen Position

			//create ray, origin is camera, and direction to mousepoint
			Camera ca;
			if (cam != null ) ca = cam.GetComponent<Camera> (); 
			else ca = Camera.main;

			Ray ray = ca.ScreenPointToRay(Input.mousePosition);

			//Return the ray's hits
			RaycastHit[] hits = Physics.RaycastAll (ray);

			foreach (RaycastHit hit in hits) {
				print (hit.transform.gameObject.name);
				if (hit.collider.gameObject.tag.Contains("Finish")) { //plane tag
					Debug.Log ("hit " + hit.collider.gameObject.name +"!" ); 
				}
				Destroy (hit.transform.gameObject);
			}
		}		
	}
}
2. 面向对象的游戏编程
2.1 场景单实例

运用模板,可以为每个 MonoBehaviour子类 创建一个对象的实例。Singleten<T> 代码如下所示:

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 needed in the scene, but there is none.");  
				}  
			}  
			return instance;  
		}  
	}
}

场景单实例的使用很简单,你仅需要将 MonoBehaviour 子类对象挂载任何一个游戏对象上即可。

然后在任意位置使用代码 Singleton<YourMonoType>.Instance 获得该对象。

二、实验部分

1.项目配置过程

新建3d-unity的文件,然后直接把gitee上Assets文件夹替换新项目的Assets文件夹,直接点击运行即可开始游戏。

2. 实现思路及核心算法

在打飞碟中,共设置了十二个函数。

1)Singleton.cs

跟如上的模版一样。

2) SSDirector.csSSAction.csSSActionManager.csInterfaces.cs

与之前的作业相差无几,不再解释。

3)UserGUI.cs

实现了初始界面和记分板。

4)FirstController.cs
  • Start()函数,完成了DiskFactory和scoreRecord单例模式和创建。
void Start () {  
        SSDirector director = SSDirector.GetInstance();     
        director.CurrentScenceController = this;
        disk_factory = Singleton<DiskFactory>.Instance;
        score_recorder = Singleton<ScoreRecorder>.Instance;
        action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
        user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;
    }
  • Update()函数是程序运行的主函数,更新了打飞碟游戏的以下参数:回合、尝试的次数、飞碟的个数。
void Update () {
        if(running) {
            count++;

            if (Input.GetButtonDown("Fire1")) {
                Vector3 pos = Input.mousePosition;
                Hit(pos);
            }
            switch (round) {
                case 1: {
                    if (count >= 600) {
                        count = 0;
                        SendDisk(trial % 2 + 1);
                        if(Random.Range(1,3) == 1)
                            SendDisk(1);
                        
                        if (trial >= 10) {
                            round += 1;
                            trial = 0;
                        }
                        trial += 1;
                    }
                    break;
                }
                case 2: {
                    if (count >= 600) {
                        if (trial == 11) {
                            running = false;
                        }
                        count = 0;
                        if(trial <= 9){
                            SendDisk(Random.Range(1,3));
                        }
                        if(Random.Range(1,2) == 1){
                            SendDisk(Random.Range(1,3));
                        }
                        if(Random.Range(1,3) == 1){
                            SendDisk(Random.Range(1,3));
                        }
                        trial += 1;
                    }
                    break;
                }
                default:
                    break;
            } 

            bool ret = disk_factory.FreeUsedDisks();
            if(!ret){
                HP--;
                if(HP == 0){
                    GameOver();
                }
            }
        }
    }

其中,检测是否有鼠标点击的代码如下:

if (Input.GetButtonDown("Fire1")) {
                Vector3 pos = Input.mousePosition;
                Hit(pos);
            }
  • Hit()函数检测是否击中飞碟。
public void Hit(Vector3 pos) {
       // Debug.Log("2");
        Ray ray = Camera.main.ScreenPointToRay(pos);
        RaycastHit[] hits;
        hits = Physics.RaycastAll(ray);
        for (int i = 0; i < hits.Length; i++) {
            RaycastHit hit = hits[i];
            if (hit.collider.gameObject.GetComponent<Disk>() != null) {
                score_recorder.RecordScore(hit.collider.gameObject);
                hit.collider.gameObject.transform.position = new Vector3(0, -15, 0);
            }
        }
    }
  • SendDisk()函数调用飞碟工厂类生成飞碟,并随机赋予速度、角度和位置:
private void SendDisk(int type) {
        GameObject disk = disk_factory.GetDisk(type);

        float disk_y = Random.Range(0f, 3f);
        float disk_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
 
        float speed = 0;
        float angle = Random.Range(15f, 25f);
        if (type == 1) {
            speed = Random.Range(1f, 1.5f);
        }
        else if (type == 2) {
            speed = Random.Range(1.5f, 2f);
        }
        
        disk.transform.position = new Vector3(disk_x * 14f, disk_y, 0);
        action_manager.DiskFly(disk, angle, speed);
    }
5)DiskFlyAction.cs

SSAction是动作基类,DiskFlyAction是飞碟飞行的动作类。

DiskFlyAction继承了SSAction,并且通过传入角度、初速度完成飞行的行为:

6)DiskFactory.cs

DiskFactory类是飞碟工厂类,作用是在需要的时候“生产”和销毁不同的飞碟。

  • 使用链表存放使用的和空闲的飞碟:
private List<Disk> BusyDisks = new List<Disk>();
private List<Disk> FreeDisks = new List<Disk>();
  • 根据预设生成飞碟:
if(type == 1) {
	disk_prefab = GameObject.Instantiate(
	Resources.Load<GameObject>("Prefabs/disk1"),
	new Vector3(0, -10f, 0), Quaternion.identity);
}else if (type == 2) {
	disk_prefab = GameObject.Instantiate(
	Resources.Load<GameObject>("Prefabs/disk2"),
	new Vector3(0, -10f, 0), Quaternion.identity);
}else {
	disk_prefab = GameObject.Instantiate(
	Resources.Load<GameObject>("Prefabs/disk3"),
	new Vector3(0, -10f, 0), Quaternion.identity);
}disk_prefab.GetComponent<Renderer>().material.color = disk_prefab.GetComponent<Disk>().color;

  • 通过返回一个bool判断飞碟是否落到屏幕外来判断是否正确击中飞碟,从而实现打飞碟的hp系统。
public bool FreeUsedDisks() {
        bool ret = true;
        for(int i = 0; i < BusyDisks.Count; i++) {
            if (BusyDisks[i].gameObject.transform.position.y + 10f > -0.2f && BusyDisks[i].gameObject.transform.position.y + 10f < 0.2f){
                Debug.Log("1");
                BusyDisks[i].gameObject.transform.position = new Vector3(0, -20f, 0);
                ret =false;
            }
            else if (BusyDisks[i].gameObject.transform.position.y <= -15f) {
                FreeDisks.Add(BusyDisks[i]);
                BusyDisks.Remove(BusyDisks[i]);
            }
        }
        return ret;
    }
7)ScoreRecorder.cs

统计分数,更新分数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity游戏制作是使用Unity引擎进行游戏开发的过程。在游戏制作中,通常会涉及到不同的层级和功能模块。持久层是负责数据存储和访问的模块,模型层是负责定义数据模型的模块,服务层是前后端沟通的服务级汇总,控制层负责处理前端操作,展现层是纯前端展现的工作。[1] 在Unity游戏制作中,大地图类用于展现大地图和实现地图传送功能,地图类用于定义地图的属性和操作,地图块类用于定义地图上的地图块属性和事件处理,NPC类用于定义NPC的属性和对话功能,玩家类用于记录玩家的属性和状态,敌人类用于定义敌人的属性和AI战斗,小队类用于管理玩家的小队,物品类用于定义游戏中的物品,动画管理类用于管理游戏中的动画,UI类用于处理场景中的UI,操作响应类用于处理摇杆和按键事件,战场类用于定义战场的属性和处理战斗逻辑,战斗玩家类用于记录战斗过程中的临时数据,buff类用于定义游戏中的buff效果。[2][3] 在Unity游戏制作中,需要考虑前端与后端的分离,如何汇总view操作,流程部分的设计,以及如何避免在不该改数据的地方改数据,在不该改界面的地方改界面等问题。同时,可以使用面向切面的方式来实现前端的切入。[1] 总之,Unity游戏制作需要综合考虑不同层级和功能模块的设计和实现,以实现游戏的功能和可用性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值