用U3D开发消灭星星(Pop Star)
某大学计算机专业的学生,接触u3d四个月了,正巧赶上学校Java有一个开发PopStar的实验课,寻思着用unity做一做。同时把这个小工程当做自己这几个月学习的一个小测试,真正的独立去开发一个小游戏,找到自己的弱点和不足之处。此外,写这个博客记录一下自己unity3d的学习。各位u3d大佬手下留情,如果能指点一二那就更好了。一,场景的搭建
1.准备工作:
1)工程创建为2D,接着各种文件夹的创建;
2)两个场景——游戏场景,菜单界面(涉及到皮肤的更换);
3)素材资源的导入;
4)素材的处理,主要是分割图片。
5)导入Dotween动画插件,方便实现星星的缓动效果。(这里应该是有点大材小用了,主要是想偷个懒 :) )
2.搭建菜单场景
1)面子工程:历史最高分,最大关卡的显示(逻辑后面再实现)。
2)主要功能:游戏的开始与退出的,皮肤切换,分数和关卡的保存。
3)创建空物体GameManager挂载脚本GameManager01实现:a.menu场景->game场景的切换和游戏的退出;
b.添加场景的序列号;
c.记录玩家选择的皮肤;
d.为Button添加鼠标点击事件;代码如下:
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager01 : MonoBehaviour
{
public Toggle toggle1;//取得皮肤1的Toggle组件
/**
* 游戏退出
*/
public void OnClickExit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
/**
* 开始游戏
*/
public void OnClickStart()
{
//开始游戏之前保存用户选择的皮肤
if (toggle1.isOn)
{
PlayerPrefs.SetInt("SkinMode",0 );//皮肤设置为1
}
else
{
PlayerPrefs.SetInt("SkinMode", 1);//皮肤设置为2
}
print(PlayerPrefs.GetInt("SkinMode"));//输出,检测功能是否实现
SceneManager.LoadScene(1);
}
}
3.搭建游戏场景
1)创建空物体StarGrid用于存放所有的星星,把其当做所有星星的父物体,统一进行管理
2)新建Image,source image选中为红色星星,添加Star脚本,并将其他四个星星都作成prefab,并为不同的星星添加tag,代码如下:
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
public class Star : MonoBehaviour
{
public int row = 0;//行值
public int col = 0;//列值
private void Start()
{
SetPos();
}
/**
* 根据行列值对星星的位置进行操作
*/
public void SetPos()
{
transform.DOLocalMove(new Vector3(-258 + col*52 + 22.5f , 258 -row*52 - 22.5f , 0),0.5f);
}
/**
* 通过行列值判断行星是否存在List中
*/
public bool IsExist(List<Star> starList,int row,int col)
{
for (int i = 0; i < starList.Count; i++)
{
Star star = starList;
if (star.row == row && star.col == col)
{
return true;
}
}
return false;
}
/**
* 清除单个星星的方法
*/
public void Clear()
{
//TODO 星星清除的粒子特效
this.transform.DOScale(new Vector3(0.01f, 0.01f, 0.01f), 0.3f);//借用了Dotween动画插件
Destroy(this);//销毁
}
}
3)添加空物体GameManager挂载GameManager02脚本用于实现:
a.星星矩阵的实例化;
private List<Star> CreateStarList()
{
List <Star> StarList = null;
for (int i = 0; i < MAX_ROW; i++)
{
for (int j = 0; j < MAX_COL; j++)
{
GameObject gameobject = Instantiate(GetRandomPrefab(), Vector3.zero, Quaternion.identity);//实例化随机颜色的星星
gameobject.transform.SetParent(starGrid);//把星星的父物体设置为starGrid
gameobject.transform.localScale = new Vector3(1,1,1);//把局部的scale保持为原来一致,防止相机渲染导致大小的变化
Star star = gameobject.GetComponent<Star>();//取得当前星星的引用
star.row = i;//设置行值
star.col = j;//设置列值
star.SetPos();//位置的变化
StarList.Add(star);//把星星添加到集合中
}
}
return StarList;
}
/**
* 随机获得五个星星prefab中的一个
*/
private GameObject GetRandomPrefab()
{
int r = Random.Range(0, 5);
GameObject prefab = redStarPrefab;
switch (r)
{
case 0:
prefab = redStarPrefab;
break;
case 1:
prefab = blueStarPrefab;
break;
case 2:
prefab = yellowStarPrefab;
break;
case 3:
prefab = purpleStarPrefab;
break;
case 4:
prefab = greenStarPrefab;
break;
}
return prefab;
}
b.星星的消除,创建空物体StarManager挂载StarManager脚本用于实现星星的消除和位置的移动;
实现获得待星星消除的方法,该方法在Star的鼠标点击事件里面进行调用。
涉及了递归调用,递归死循环已经让我绝望了无数次了。
具体的算法也不进行介绍了,还算比较简单
public static void FindStar(List<Star> currentStarList, Star star, List<Star> clearedStars)
{
int row = star.row;
int col = star.col;
Star tStar = null;
if (col - 1 >= 0)
{
tStar = Star.LookupStar(currentStarList, row, col - 1);
if (tStar != null && !Star.IsExisted(clearedStars, tStar))
{
if (tStar.tag == star.tag)
{
clearedStars.Add(tStar);
FindStar(currentStarList, tStar, clearedStars);
}
}
}
if (col + 1 < GameManager02.MAX_COL)
{
tStar = Star.LookupStar(currentStarList, row, col + 1);
if (tStar != null && !Star.IsExisted(clearedStars, tStar))
{
if (tStar.tag == star.tag)
{
clearedStars.Add(tStar);
FindStar(currentStarList, tStar, clearedStars);
}
}
}
if (row - 1 >= 0)
{
tStar = Star.LookupStar(currentStarList, row - 1, col);
if (tStar != null && !Star.IsExisted(clearedStars, tStar))
{
if (tStar.tag == star.tag)
{
clearedStars.Add(tStar);
FindStar(currentStarList, tStar, clearedStars);
}
}
}
if (row + 1 < GameManager02.MAX_ROW)
{
tStar = Star.LookupStar(currentStarList, row + 1, col);
if (tStar != null && !Star.IsExisted(clearedStars, tStar))
{
if (tStar.tag == star.tag)
{
clearedStars.Add(tStar);
FindStar(currentStarList, tStar, clearedStars);
}
}
}
}
c.皮肤的切换,更换图片的Sprite就好了
/**
*修改皮肤的方法
*/
private void ChangeSkin()
{
if (PlayerPrefs.GetInt(&#34;SkinMode&#34;) == 0)
{
background.GetComponent<Image>().sprite = skinSprites;
redStarPrefab.GetComponent<Image>().sprite = starSprites;
blueStarPrefab.GetComponent<Image>().sprite = starSprites;
yellowStarPrefab.GetComponent<Image>().sprite = starSprites;
purpleStarPrefab.GetComponent<Image>().sprite = starSprites;
greenStarPrefab.GetComponent<Image>().sprite = starSprites;
}
if (PlayerPrefs.GetInt(&#34;SkinMode&#34;) == 1)
{
background.GetComponent<Image>().sprite = skinSprites;
redStarPrefab.GetComponent<Image>().sprite = starSprites;
blueStarPrefab.GetComponent<Image>().sprite = starSprites;
yellowStarPrefab.GetComponent<Image>().sprite = starSprites;
purpleStarPrefab.GetComponent<Image>().sprite = starSprites;
greenStarPrefab.GetComponent<Image>().sprite = starSprites;
}
}
d.星星消除后的位置移动
上下的移动,核心算法就不介绍了,简单易懂。
/**
* 更新上下,并且返回空列的col的集合,以便进行左右的移动
*/
public static List<int> UpdateUD()
{
List<int> nullColumn = new List<int>();
for (int i = 0; i < GameManager02.MAX_COL; i++)
{
int moveStep = 0;
for (int j = GameManager02.MAX_ROW - 1; j >= 0; j--)
{
Star star = Star.LookupStar(StarManager.starList, j, i);
if (star == null)
{
moveStep++;
continue;
}
else
{
if (moveStep == 0)
{
continue;
}
else
{
star.row += moveStep;
star.SetPos();
}
}
}
if (moveStep == GameManager02.MAX_ROW)
{
nullColumn.Add(i);
}
}
if (nullColumn != null)
{
for (int i = 0; i < nullColumn.Count; i++)
{
print(nullColumn + &#34; &#34;);
}
}
return nullColumn;
}
左右的移动,基于上下移动
/**
*更新左右,传入空列的集合
*/
public static void UpdateLR(List<int> nullColumn)
{
int moveStep = 0;
for (int i = 0; i < GameManager02.MAX_COL; i++)
{
if (nullColumn.Contains(i))
{
moveStep++;
}
else
{
if (moveStep == 0)
{
continue;
}
else
{
for (int j = GameManager02.MAX_ROW - 1; j >= 0; j--)
{
Star star = Star.LookupStar(StarManager.starList, j, i);
if (star != null)
{
star.col -= moveStep;
star.SetPos();
}
}
}
}
}
}
游戏逻辑的实现都在Star脚本里面完成,鼠标点击开始时调用函数。
到了这一步游戏的主要功能就差不多完成了。
二,分数计算的实现,以及存档的保存
根据一次消除星星的个数进行计算分数:10*(Mathf.pow(2,number-2)),最高次定为8
canvas下新建空物体ScoreManager用于挂载ScoreManager脚本进行分数的计算
public float ComputeScore(int number)
{
if (number<8)
{
return 10 * Mathf.Pow(2, number);
}
else
{
return 10 * Mathf.Pow(2, 8);
}
}
三,动画的制作和画面的优化
当当前分数>目标分数,通关成功,加在下一关;
反之闯关败,记录分数和关卡,更新最高分等信息并返回主菜单
主动画的制作要是面板显示的的渐入渐出
/**
* 根据剩余星星数,给予额外得分
*/
public void GetBonusScore(List<Star> starList)
{
if (starList.Count>20)
{
return;
}
else
{
float bonusScore = 10 * (20 - starList.Count);
score += bonusScore;
}
UpdateScoreText();
}
/**
*根据关卡数计算目标分数
*/
public void GetGoalScore(int number)
{
goalScore = 1000 * number + 2000 * (number / 3 + 1);
}
/**
*更新UI
*/
public void UpdateScoreText()
{
scoreText.text = &#34;当前分数:&#34; + score;
goalScoreText.text = &#34;目标分数:&#34; + goalScore;
levelText.text = &#34;当前关卡:&#34; + level;
}
public void GetBounsUI(int number)
{
float bonusScore = 10 * (20 - number);
bouns.gameObject.SetActive(true);
bouns.text = &#34;剩余:&#34; + number + &#34;额外得分&#34; + bonusScore;
}
5.音效的添加和粒子系统的完善。更改渲染模式为world space,手残党。粒子特效太硬核了,就演示了。
6.导出游戏,工程结束。
一小段演示视屏:
https://www.zhihu.com/video/1131908748255662080 牛 为啥劝退u3d? [赞][赞][赞] 好!!! 哈哈,听起来好玩而已,没其他意思 楼主可以分享一下源代码吗?正好要学习制作消灭星星,有许多不懂的问题 核心的代码基本上都在里面了 LookupStar是什么方法? 同问 LookupStar是什么方法?
页:
[1]
2