本科课程【虚拟现实引擎Unity3D】实验4 - 卡牌游戏完善

x33g5p2x  于2022-05-16 转载在 其他  
字(8.1k)|赞(0)|评价(0)|浏览(496)

大家好,我是【1+1=王】, 热爱java的计算机(人工智能)渣硕研究生在读。
如果你也对java、人工智能等技术感兴趣,欢迎关注,抱团交流进大厂!!!
Good better best, never let it rest, until good is better, and better best.

近期会把自己本科阶段的一些课程设计、实验报告等分享出来,供大家参考,希望对大家有帮助。

博客更新至专栏【课程设计实验报告】:https://blog.csdn.net/weixin_43598687/category_11640051.html

一、 实验目的

  1. 掌握虚拟现实游戏开发的基本流程

二、 实验内容

1. 实验任务

修改第10章卡牌游戏,将牌面改成9行8列,每种花色的牌面编号为1-9,同一花色同一排面编号的牌出现两次。加入计时、计分、及时间限制功能,牌面配对成功、失败时的音效提示,在限制时间内完成全部配对或未完成全部配对的音效提示,能确定结束游戏或重玩游戏。

2. 程序设计

1) 数据输入(输入哪些数据、个数、类型、来源、输入方式)
初始化输入以及Unity界面参数设置

2) 数据存储(输入数据在内存中的存储)
(1)卡牌控制的数据存储:int _id存储卡牌的编号数据,游戏对象cardBack存储游戏对象,场景控制器对象controller存储控制器对象;
(2)游戏控制的数据存储:int gridRows和int gridCols分别存储网格中的行和列,offsetX和offsetY分别存储卡牌的x,y坐标,originateCard存储原始卡牌,images[]数组存储卡牌图片数据,scoreLabel存储TextMesh中的文本信息,_firstRevealed和_secondRevealed分别存储第一张和第二张翻转的卡牌信息,_score存储分数,number[]数组存储洗牌清单,card存储网格中的卡牌信息,index存储网格的位置;
(3)游戏界面控制的数据存储:targetObject存储目标对象信息,targetMessage存储发送的消息,highlightColor存储颜色信息;
(4)摄像机移动的数据存储:没有数据存储

3) 数据处理
(1)卡牌控制的数据处理:
id()方法用于获取卡牌的编号

public int id {
		get {return _id;}
	}

显示不同的卡牌图像时,Unity 3D通过程序加载图像,更换Sprite Renderer中的精灵图片,采用GetComponent().sprite = image;语句实现;
通过不可见的SceneController来设置图像MemoryCard.cs中的新公有方法

[SerializeField] private SceneController controller;

	private int _id;
	public int id {
		get {return _id;}
	}

	public void SetCard(int id, Sprite image) {
		_id = id;
		GetComponent<SpriteRenderer>().sprite = image;
	}
OnMouseDown()方法实现匹配得分和显示卡牌
	public void OnMouseDown() {
		if (cardBack.activeSelf && controller.canReveal) {
			cardBack.SetActive(false);
			controller.CardRevealed(this);
		}
	}
Unreveal()方法实现卡牌的翻转
	public void Unreveal() {
		cardBack.SetActive(true);
	}

(2)游戏控制的数据处理:
通过不可见的SceneController来设置图像创建空对象绑定SceneController.cs

[SerializeField] private MemoryCard originalCard;
	[SerializeField] private Sprite[] images;
	void Start() {
				int index = j * gridCols + i;
				int id = numbers[index];
				card.SetCard(id, images[id]);
}
实例化一个网格的卡牌,8次复制一个卡牌并定位到一个网格中
		for (int i = 0; i < gridCols; i++) {
			for (int j = 0; j < gridRows; j++) {
				MemoryCard card;

				// use the original for the first grid space
				if (i == 0 && j == 0) {
					card = originalCard;
				} else {
					card = Instantiate(originalCard) as MemoryCard;
				}

				// next card in the list for each grid space
				int index = j * gridCols + i;
				int id = numbers[index];
				card.SetCard(id, images[id]);

				float posX = (offsetX * i) + startPos.x;
				float posY = -(offsetY * j) + startPos.y;
				card.transform.position = new Vector3(posX, posY, startPos.z);
			}
打乱卡牌并且使每种卡牌都有两张
        int[] numbers = { 0, 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, 0, 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 };
		numbers = ShuffleArray(numbers);
实现匹配、得分、和翻开一对卡牌
	private MemoryCard _firstRevealed;
	private MemoryCard _secondRevealed;
	private int _score = 0;

	public bool canReveal {
		get {return _secondRevealed == null;}
	}
保存并比较翻开的卡牌
	public void CardRevealed(MemoryCard card) {
		if (_firstRevealed == null) {
			_firstRevealed = card;
		} else {
			_secondRevealed = card;
			StartCoroutine(CheckMatch());
		}
Knuth洗牌算法
	private int[] ShuffleArray(int[] numbers) {
		int[] newArray = numbers.Clone() as int[];
		for (int i = 0; i < newArray.Length; i++ ) {
			int tmp = newArray[i];
			int r = Random.Range(i, newArray.Length);
			newArray[i] = newArray[r];
			newArray[r] = tmp;
		}
		return newArray;
	}

(3)游戏界面控制的数据处理:
从SceneController中调用LoadLevel按钮的SendMessage尝试调用SceneController中的Restart方法

public void Restart() {
		Application.LoadLevel("Scene");
	}
OnMouseEnter()方法实现对游戏界面中的按钮控制,当鼠标滑到按钮上执行的操作
	public void OnMouseEnter() {
		SpriteRenderer sprite = GetComponent<SpriteRenderer>();
		if (sprite != null) {
			sprite.color = highlightColor;
		}
OnMouseExit()方法实现对游戏界面中的按钮控制,当鼠标离开按钮执行的操作
	public void OnMouseExit() {
		SpriteRenderer sprite = GetComponent<SpriteRenderer>();
		if (sprite != null) {
			sprite.color = Color.white;
		}
	}
OnMouseDown()方法实现对游戏界面中的按钮控制,当鼠标按下按钮执行的操作
	public void OnMouseDown() {
		transform.localScale = new Vector3(1.1f, 1.1f, 1.1f);
	}
OnMouseUp()方法实现对游戏界面中的按钮控制,当鼠标松开按钮执行的操作
	public void OnMouseUp() {
		transform.localScale = Vector3.one;
		if (targetObject != null) {
			targetObject.SendMessage(targetMessage);
		}
	}

(4)摄像机移动的数据处理:
控制摄像机的上下移动,使其可以点击超出运行界面的卡牌

void Update()
    {
        if( Input.GetAxis("Mouse ScrollWheel") > 0 )
        {
            transform.Translate(Vector3.up * Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * 500f);
        }
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            transform.Translate(-Vector3.down * Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * 500f);
        }
    }

4) 数据输出
初始界面:可以通过鼠标滚动显示下面几行的牌


当鼠标放在按钮之上的界面:按钮颜色发生变化

按下按钮之后的界面:按钮大小发生变化

翻牌之后的界面:;两张扑克牌花色不同时不能得分

两张扑克牌的花色相同数字不同时同样不能得分

两张牌的花色和数字都匹配时即可得分,并且牌不会翻回去


得分之后牌面不会翻转,可以继续翻其他的牌

三、 实验环境

  1. 操作系统:WINDOWS 10
  2. 开发工具:Unity3D
  3. 实验设备:PC

源代码

MemoryCard:
using UnityEngine;
using System.Collections;

public class MemoryCard : MonoBehaviour {
	[SerializeField] private GameObject cardBack;
	[SerializeField] private SceneController controller;

	private int _id;
	public int id {
		get {return _id;}
	}

	public void SetCard(int id, Sprite image) {
		_id = id;
		GetComponent<SpriteRenderer>().sprite = image;
	}

	public void OnMouseDown() {
		if (cardBack.activeSelf && controller.canReveal) {
			cardBack.SetActive(false);
			controller.CardRevealed(this);
		}
	}

	public void Unreveal() {
		cardBack.SetActive(true);
	}
}
SceneController:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class SceneController : MonoBehaviour {
	public const int gridRows = 9;
	public const int gridCols = 8;
	public const float offsetX = 2f;
	public const float offsetY = 2.5f;
	[SerializeField] private MemoryCard originalCard;
	[SerializeField] private Sprite[] images;
	[SerializeField] private TextMesh scoreLabel;
	
	private MemoryCard _firstRevealed;
	private MemoryCard _secondRevealed;
	private int _score = 0;

	public bool canReveal {
		get {return _secondRevealed == null;}
	}

	// Use this for initialization
	void Start() {
		Vector3 startPos = originalCard.transform.position;

		// create shuffled list of cards
        int[] numbers = { 0, 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, 0, 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 };
		numbers = ShuffleArray(numbers);

		// place cards in a grid
		for (int i = 0; i < gridCols; i++) {
			for (int j = 0; j < gridRows; j++) {
				MemoryCard card;

				// use the original for the first grid space
				if (i == 0 && j == 0) {
					card = originalCard;
				} else {
					card = Instantiate(originalCard) as MemoryCard;
				}

				// next card in the list for each grid space
				int index = j * gridCols + i;
				int id = numbers[index];
				card.SetCard(id, images[id]);

				float posX = (offsetX * i) + startPos.x;
				float posY = -(offsetY * j) + startPos.y;
				card.transform.position = new Vector3(posX, posY, startPos.z);
			}
		}
	}

	// Knuth shuffle algorithm
	private int[] ShuffleArray(int[] numbers) {
		int[] newArray = numbers.Clone() as int[];
		for (int i = 0; i < newArray.Length; i++ ) {
			int tmp = newArray[i];
			int r = Random.Range(i, newArray.Length);
			newArray[i] = newArray[r];
			newArray[r] = tmp;
		}
		return newArray;
	}

	public void CardRevealed(MemoryCard card) {
		if (_firstRevealed == null) {
			_firstRevealed = card;
		} else {
			_secondRevealed = card;
			StartCoroutine(CheckMatch());
		}
	}
	
	private IEnumerator CheckMatch() {

		// increment score if the cards match
		if (_firstRevealed.id == _secondRevealed.id) {
			_score++;
			scoreLabel.text = "Score: " + _score;
		}

		// otherwise turn them back over after .5s pause
		else {
			yield return new WaitForSeconds(.5f);

			_firstRevealed.Unreveal();
			_secondRevealed.Unreveal();
		}
		_firstRevealed = null;
		_secondRevealed = null;
	}

	public void Restart() {
		Application.LoadLevel("Scene");
	}
}
UIButton:
using UnityEngine;
using System.Collections;

public class UIButton : MonoBehaviour {
	[SerializeField] private GameObject targetObject;
	[SerializeField] private string targetMessage;
	public Color highlightColor = Color.cyan;

	public void OnMouseEnter() {
		SpriteRenderer sprite = GetComponent<SpriteRenderer>();
		if (sprite != null) {
			sprite.color = highlightColor;
		}
	}
	public void OnMouseExit() {
		SpriteRenderer sprite = GetComponent<SpriteRenderer>();
		if (sprite != null) {
			sprite.color = Color.white;
		}
	}

	public void OnMouseDown() {
		transform.localScale = new Vector3(1.1f, 1.1f, 1.1f);
	}
	public void OnMouseUp() {
		transform.localScale = Vector3.one;
		if (targetObject != null) {
			targetObject.SendMessage(targetMessage);
		}
	}
}
CameraMove:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraMove : MonoBehaviour
{
    void Update()
    {
        if( Input.GetAxis("Mouse ScrollWheel") > 0 )
        {
            transform.Translate(Vector3.up * Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * 500f);
        }
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            transform.Translate(-Vector3.down * Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * 500f);
        }
    }
}

博客更新至专栏【课程设计实验报告】:https://blog.csdn.net/weixin_43598687/category_11640051.html

相关文章