Hi I'm Shendi
开始首页的制作
首页设计
首页,即用户打开后见到的第一个页面。
设计是比较头疼的,根据上节的策划,主页里有大概以下按钮
- 开始
- 商店
- 历史分数
游戏名称也是要有的,一般都放在最开头
背景的话,就用不断下坠的方块好了
再加一个关于的按钮用于写一些格外信息
有了想法,就开始制作了
首页制作开始
打开项目,当前摄像机是横屏的,调整成竖屏
点击 Game,选择分辨率,如果没有竖屏的分辨率那就手动加一个,例如 720*1280
将当前项目平台切换为WebGL,按下Ctrl+Shift+B,或者点击文件 - 生成设置,找到WebGL,右边有unity图标代表已经是使用了,如果没有就点击右下角的切换平台
制作背景
首先把背景制作出来,背景嘛,就从上往下掉落方块,生成速度和x位置随机
在层级界面右键,创建空对象,命名为Background
将这个空对象作为背景,拉到摄像头最上方
新建一个C#脚本,命名为Background,然后将脚本拖到层级面板的Background上
双击脚本打开,如果没有VisualStudio则先下载安装,里面选择C#和Unity
有的话,则点击编辑 - 首选项,外部工具,编辑器选择Visual Studio
新建完后代码是如下的
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Background : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
其中Start是脚本第一次加载时执行,且只执行一次,Update是每一帧都执行
制作小方块(预制体)
在层级面板右键,2D对象,
创建后将方块缩放调整到合适的大小,例如0.5x0.5
在层级面板将物体重命名,新建 Resources 文件夹,在文件夹内新建Prefabs文件夹,在层级面板将物体拖到Prefabs文件夹,就成了预制体了
创建状态类(工具类)
游戏必然会有很多的状态,例如现在制作首页,背景是一直生成方块往下移动,当打开商店或者开始游戏时,背景这个操作就需要暂停
新建一个脚本文件,作为全局属性类,例如State类
代码如下
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
/**
* 包含全局属性/状态
*/
public class State
{
/** 背景是否播放 */
public static bool isBackgroundPlay = true;
#region 预制体部分
/** 小方块 */
public static GameObject box = Resources.Load<GameObject>("Prefabs/方块");
/** 获取随机的小方块,通过box物体 */
public static GameObject createRanBox()
{
GameObject obj = GameObject.Instantiate(box);
return obj;
}
#endregion
}
最上面一行的 isBackgroundPlay
代表控制背景是否继续生成小方块的变量,true继续,false停止
上面的 GameObject
类代表游戏对象,Resources.Load 是加载 Resources
文件夹内的文件,我们的预制体在 Prefabs
文件夹下,名称为方块,于是这样加载预制体
createRanBox 是随机生成方块,其中的 GameObject.Instantiate
是将预制体实例化(放到游戏场景中)
目前这个函数仅仅是创建了预制体,并返回预制体实例,先看下效果,后面会增加方块数量以及颜色
生成一个小方块
将Background类的start调用createRanBox函数,运行看下效果
void Start()
{
State.createRanBox();
}
运行前
运行后,可以看到多出了一个方块了
但是这个物体不是Background对象子对象,于是将 Background 的 start 改成以下代码
void Start()
{
State.createRanBox().transform.parent = transform;
}
transform是获取到物体的 Transform 组件,parent 就是父对象,当前Background脚本绑定在了Background对象上,于是直接使用 transform 代表 Background 的 Transform,也就是新生成的小方块的父物体是Background
运行,可以看到方块变成了Background的子物体了
背景蓝色有点违和,点击 Main Camera,调整一下颜色
方块整体的生成
一个方块整体由多个小方块组成,方块颜色和小方块数量随机,这里试了下,横排最多10个方块,竖着不超过3方块,于是限制生成的方块的小方块数量范围为 1-15
首先更改以下 State 内的 createRanBox 函数,首字母大写(C#的规范)
将简单的随机生成颜色,数量制作出来
简单介绍一下随机数的使用 Random.Range(0, 2);,代表随机0和1,小数的话就0(包括)到1(不包括)之间的任何小数
代码如下
public static GameObject CreateRanBox()
{
// 随机小方块数量,随机颜色
var ranNum = Random.Range(1, 16);
var ranColor = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
GameObject parentObj = new GameObject("方块集");
for (int i = 0; i < ranNum; i++)
{
GameObject obj = GameObject.Instantiate(box);
var render = obj.GetComponent<Renderer>();
render.material.color = ranColor;
// 随机位置
obj.transform.parent = parentObj.transform;
}
return parentObj;
}
剩下的就是随机位置,不重合,并且方块有相邻方块即可
我的思路是,先创建第一个小方块,记录这个方块的位置,其余方块都根据这个方块的位置来随机上下左右生成
限制一下,第一个方块在最顶部,因为是Background的子物体,那么就是y为0,高度不超过三方块,于是y只能为0,1,-1,x的话,经尝试,x为-2.55和2.55正好到达屏幕两端,小方块大小是0.5
于是制作一个二维数组来记录位置
第二维代表y,大小为3(0=-0.5,1=0,2=0.5)
第一维代表x,大小为11,其中5代表中心为0(0=-2.5,1=-2,2=-1.5...5=0,6=0.5,7=1...10=2.5)
值为位置信息 Vector2
,空代表这个位置没有方块。
这里需要注意下,Vector2默认值为 Vector2.zero
这样思路就清晰许多了:
从中心方块出发,随机在中心方块上下左右生成方块,如果有则换方向,没有则记录生成的位置,下次使用这个位置继续随机上下左右生成,如果上下左右都没有可生成的地方了,就又从中心方块出发随机..并且需要判断当前y位置的x方块是否大于一半(也就是6个)
开始编写代码,定义中心位置
/** 小方块数组中心位置 */
private const int xCenter = 5, yCenter = 1;
CreateRanBox
函数代码如下
public static GameObject CreateRanBox()
{
// 随机小方块数量,随机颜色
var ranNum = Random.Range(1, 16);
var ranColor = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
GameObject parentObj = new GameObject("方块集");
// 记录已经使用过的位置
Vector2[,] posMap = new Vector2[3,11];
Vector2 centerPos = Vector2.zero;
// 上一次位置
int upX = -1, upY = -1;
for (int i = 0; i < ranNum; i++)
{
GameObject obj = GameObject.Instantiate(box);
var render = obj.GetComponent<Renderer>();
render.material.color = ranColor;
obj.transform.parent = parentObj.transform;
// 记录第一个位置
if (i == 0) {
centerPos = new Vector2(Random.Range(-2.55f, 2.56f), 0);
posMap[yCenter, xCenter] = centerPos;
obj.transform.localPosition = centerPos;
} else {
if (upX == -1 || upY == -1)
{
upX = xCenter;
upY = yCenter;
}
// 随机上下左右,0上,1下,2左,3右
List<int> dirs = new List<int>();
dirs.Add(0); dirs.Add(1); dirs.Add(2); dirs.Add(3);
for (int j = 0; j < 4; j++)
{
int direction = dirs[Random.Range(0, dirs.Count)];
// x减往左,加往右,y减往下,加往上
switch (direction)
{
case 0: {
int y = upY + 1;
if (y >= 3 || posMap[y, upX] != Vector2.zero)
{
dirs.Remove(direction);
}
else
{
// 1代表0.5大小,以数组中间点为界x=5,y=1,得出差距,然后*0.5得到实际位置
float realX = (upX - 5) * 0.5f, realY = (y - 1) * 0.5f;
posMap[y, upX] = new Vector2(centerPos.x + realX, centerPos.y + realY);
obj.transform.localPosition = posMap[y, upX];
upY = y;
goto posforEnd;
}
break;
}
case 1: {
int y = upY - 1;
if (y < 0 || posMap[y, upX] != Vector2.zero)
{
dirs.Remove(direction);
}
else
{
float realX = (upX - 5) * 0.5f, realY = (y - 1) * 0.5f;
posMap[y, upX] = new Vector2(centerPos.x + realX, centerPos.y + realY);
obj.transform.localPosition = posMap[y, upX];
upY = y;
goto posforEnd;
}
break;
}
case 2: {
int x = upX - 1;
if (x < 0 || posMap[upY, x] != Vector2.zero)
{
dirs.Remove(direction);
}
else
{
float realX = (x - 5) * 0.5f, realY = (upY - 1) * 0.5f;
// x位置是否超出宽度 -2.55和2.55
float resultX = centerPos.x + realX;
if (resultX < -2.55 || resultX > 2.55)
{
dirs.Remove(direction);
break;
}
posMap[upY, x] = new Vector2(resultX, centerPos.y + realY);
obj.transform.localPosition = posMap[upY, x];
upX = x;
goto posforEnd;
}
break;
}
case 3: {
int x = upX + 1;
if (x >= 11 || posMap[upY, x] != Vector2.zero)
{
dirs.Remove(direction);
}
else
{
float realX = (x - 5) * 0.5f, realY = (upY - 1) * 0.5f;
// x位置是否超出宽度 -2.55和2.55
float resultX = centerPos.x + realX;
if (resultX < -2.55 || resultX > 2.55)
{
dirs.Remove(direction);
break;
}
posMap[upY, x] = new Vector2(resultX, centerPos.y + realY);
obj.transform.localPosition = posMap[upY, x];
upX = x;
goto posforEnd;
}
break;
}
}
}
// 如果没有合适方向的话从中心方向开始
if (dirs.Count == 0)
{
// 直接销毁当前方块,初始化值,然后重新开始本次循环即可
GameObject.Destroy(obj);
// 避免死循环,直接少生成一个方块了
// i--;
upX = -1;
upY = -1;
}
posforEnd:;
}
}
return parentObj;
}
运行后效果如下
虽然效果并不是很好,但是至少达到了
在调试的时候需要注意死循环等问题,开启vs的附加到Unity功能,死循环就停止,可以避免unity卡死导致只能任务管理器关闭重新打开的问题
让方块往下落
方块已经创建好了,接着让其往下落就可以了
运行后发现方块不是在最顶端,于是首先给拿到的物体设置一下位置
Background
中
var obj = State.CreateRanBox();
obj.transform.parent = transform;
// 有父对象则使用localPosition
obj.transform.localPosition = Vector2.zero;
因为是用作背景,往下落的话使用 Rigidbody 组件就可以了
Rigidbody,刚体组件,可以模拟物体受重力的影响
用代码给方块增加刚体组件,给速度设一个随机值
Rigidbody rigidbody = obj.AddComponent<Rigidbody>();
rigidbody.velocity = Vector3.down * Random.Range(0.1f,2f);
运行后可以看到方块往下掉落了
定时创建方块
接下来只需要定时创建方块然后让其往下落就实现背景了
直接放在 Update
函数是不可取的,一秒执行60次太多了,不需要那么多方块
使用 InvokeRepeating
函数可以在给定的时间间隔内重复调用该函数或代码
0-1.8秒之间随机生成一个方块好了,2秒后销毁,代码如下 (Background中)
void Start()
{
InvokeRepeating("CreateRanBox", 0, Random.Range(0, 1.8f));
}
/** 创建方块,三秒后销毁 */
void CreateRanBox()
{
if (State.isBackgroundPlay) {
var obj = State.CreateRanBox();
obj.transform.parent = transform;
obj.transform.localPosition = Vector2.zero;
Rigidbody rigidbody = obj.AddComponent<Rigidbody>();
rigidbody.velocity = Vector3.down * Random.Range(0.1f, 2f);
Destroy(obj, 2);
}
}
运行后即可看到源源不断下落的方块了
制作界面
制作完背景后,就开始制作主页具体按钮了
但背景有点显眼,可以做一个全屏半透明遮罩
半透明遮罩
在层级面板右键 - UI - 图像,创建UI界面
创建后会发现有一个很大很大的框框,缩放,看到一个小正方形,和一个风车一样的东西
将小正方形拉满整个方框,然后把中间那个风车一样的东西(四个三角形)拉到四个角,这样就可以自适应了
在点击正方形,在右边找到Image属性,点击颜色,拖拽其中A部分(透明的),调整为想要的透明,遮罩就做好了
可以给这个图片命个名称...
名称与按钮制作
游戏名称为:跳跳快乐方块
竖屏,于是直接将名称放到最上方就可以了
UI都放到Canvas里面,于是右键Canvas - UI - 文本
因为我是新版,选择的是 UI - 旧版 - 文本
调整一下位置,设置一下字体大小,样式,对齐,颜色,然后把那风车一样的四个三角形分期文本四个角
这样,标题就制作完成了
开始制作按钮,在最开始设计的是有四个按钮
- 开始
- 商店
- 历史分数
- 关于游戏
同样的,层级面板 Canvas 右键 - UI - 按钮,就创建了一个按钮
同样,我使用的也是旧版的按钮
创建按钮会自动创建一个文本,在按钮的里面
更改这个文本即可更改按钮的文本内容,更改为开始游戏,调整一下字体大小与按钮大小,调整一下位置,然后将四个三角形对准按钮的四个角就可以了
然后创建剩下的三个按钮
运行效果如下
至此,首页制作完成
本文链接:https://sdpro.top/blog/html/article/1061.html
♥ 赞助 ♥
尽管去做,或许最终的结果不尽人意,但你不付出,他不付出,那怎会进步呢?