创建 Da○saba○bar(第一期)
![]()
“每周省钱妙招”创刊号!
我以DeAgostini式的突兀方式开篇。
好久不见。
我是系统开发部的松山。
我在手机上浏览 Facebook 或漫画应用时,
经常会看到“Da○sabur○ber”的广告。
我不讨厌这类混乱的游戏
,所以决定自己尝试做一个看看。
顺便说一下,我只玩过试玩版,所以
如果和正式版有很大不同,请见谅。
大致要求
照例,我们先列出游戏要求:
① 游戏采用二维网格布局;
② 玩家角色可通过触摸屏幕的上下左右方向键进行控制;
③ 游戏包含多种武器和关卡;
④ 敌人会随机出现并向玩家移动;
⑤ 游戏对玩家、敌人和武器都设有碰撞检测;
⑥ 击败敌人后会显示经验值,收集经验值可以获取新武器或提升现有武器的等级。
目前就这些了。
细节方面我们会边做边完善。
时间测量、音效和其他制作细节都将是未来的任务(同时我们也委婉地暗示,我们可能根本不会做这些)。
项目创建
我已经很久没用 Unity 了,所以我们来安装最新版本。看起来
2022.3.18f1 是最新的 LTS 版本,我就安装这个版本。Unity
Hub 真方便。
我把项目命名为“BeBeSurvivor”,并创建一个 2D 应用。
资源
我会在资源商店里找到制作所需的素材(精灵图)。
在浏览免费 2D 素材的时候,我正好找到了我想要的东西。
◾️亡灵幸存者素材包:
此素材包包含所有必要的素材,例如玩家、敌人和武器。
它甚至可以直接使用,所以你几乎可以用它完成整个项目
,但我还是会很感激地使用这些素材。

◾️低多边形纹理包:
这是一个用于覆盖田野的图像包。
它也包含在亡灵幸存者资源包中,但我们不妨也试试使用这个单独的纹理包。

使用包管理器导入这两个文件。
铺设场地
低多边形材质包里有很多素材,所以我只挑选了一些看起来比较有用的。
它们尺寸有点大,所以我把它们缩小了。
就像这样。

我们将把它转换成预制件,然后用脚本把它放置在合适的位置。
大概像这样。

脚本内容如下:
// 生成一个范围从 (-50, -50, 0.5) 到 (50, 50, 0.5) 的场(5 x 5 网格) Vector2 pos = new Vector2(-50, 50); float size = 5f; int cnt = 20; for(int x = 0; x <= cnt; x++) { for(int y = 0; y <= cnt; y++) { // 如果 x 和 y 均为 0 且 cnt 为 0,则生成 field[1],否则生成 field[0] int idx = (x == 0 || x == cnt || y == 0 || y == cnt) ? 1 : 0; // 为中心网格生成 field[2] if(x == cnt / 2 && y == cnt / 2) { idx = 2; } // 生成游戏对象 obj = Instantiate(field[idx], new Vector3(pos.x + (x * size), pos.y - (y * size), 0.5f), Quaternion.identity); obj.transform.parent = parent.transform; } }
我会尝试只在外边缘和中心放置不同的图片。
目前,我暂时使用有限的显示区域,但我认为实际上无限滚动会更好。
(不过,即使是我也不得不承认,这段代码写得相当随意……)
玩家
幸存者素材包里有一些不错的素材,所以我打算用它们。

由于有现成的动画模式,请创建待机、移动和死亡动画,并
在动画器中进行设置。

这是它移动时的样子(待机状态)

运动(输入系统)
这可以控制玩家角色的移动。
图中显示角色会朝着你点击的方向移动,屏幕中心为原点。
在我离开 Unity 的这段时间里, “输入系统”的 发布了
我也会从包管理器中导入它。
我简单试了一下,但输入操作的信息似乎无法正常工作。
目前,我先跳过这个操作,继续使用传统的脚本控制方式来实现。
(等有时间我会再尝试调整输入操作。)
var mouse = Mouse.current; if(mouse.press.ReadValue() == 1) // 按下状态 { if(holdTime > 0.0f) // 等待 { holdTime -= Time.deltaTime; if(holdTime < 0.0f) holdTime = 0.0f; } else // 移动 { Vector2 pos = mouse.position.ReadValue(); } } else // 释放状态 { holdTime = HoldTime; }
可以使用 `mouse.current` 获取操作信息。
点击状态可以通过 `mouse.press.ReadValue()` 获取,点击时返回 1,释放时返回 0。
点击一段时间后,使用 `mouse.position.ReadValue()` 获取点击坐标,并
计算移动方向(向量)。大致如下所示。

由于两点的坐标已知,因此可以通过简单的减法计算向量。
长度无关紧要,所以我们将使用 `normalized` 对其进行归一化。
Vector2 vec = (pos - center).normalized;
如果将此值设置为玩家的坐标(localPosition),角色就会移动。
就像这样。(待机↔移动)

趁此机会,只提取移动方向的 X 分量,并改变角色的方向(绕 y 轴旋转 180°)。
摄像机跟踪
在这种情况下,角色会移出屏幕,所以我们需要让摄像机跟随角色移动,使角色始终位于屏幕中心。你只需要将
主摄像机的变换的 localPosition 设置为与角色的坐标相匹配即可。

代码如下所示。请注意,z 坐标固定为相机坐标。
// 跟随摄像机 Vector3 pos = playerController.GetPosition(); pos.z = -1.0f; mainCamera.gameObject.transform.localPosition = pos;
实现后效果如下:

敌人
目前稻草人就够用了,所以我先随机放置一些敌人。
幸存者素材包里有一些不错的素材,我会用那些。(谢谢!)

由于有现成的动画模式,请创建移动和死亡动画,并
在动画器中进行设置。

目前,我就先这样吧。

碰撞检测
接下来我们将进行碰撞检测。
我们将使用 Unity 默认提供的 Collider2D 碰撞检测器。
首先,我们来做准备工作。
给玩家和敌人各添加一个 2D 圆形碰撞器。
确保勾选了 IsTrigger 属性。

另外,给玩家添加一个 2D 刚体。(敌人不需要)
由于这次不需要进行物理计算,请将刚体类型设置为 Kinematic。

我们将使用脚本实现碰撞检测。
请将以下函数添加到玩家和敌人的代码中。(与触发器相关)
private void OnTriggerEnter2D(Collider2D collision) { // 碰撞器撞击时调用 // 如果撞击到敌人,则造成伤害 if (collision.tag == "Enemy") { Debug.Log("玩家被击中:" + collision.name); } } private void OnTriggerStay2D(Collider2D collision) { // 碰撞器保持接触时持续调用 } private void OnTriggerExit2D(Collider2D collision) { // 碰撞器离开时调用 }
我们将通过标签来确定被击中的目标。
玩家标签默认已经注册,所以我们将添加敌人和武器标签。
我们将为每个对象设置标签。

如果让它在接触到目标时输出日志,日志内容将如下所示。

本月内容就到这里。
今天就到这里。
我觉得我们已经基本实现了基本机制。Unity
拥有所有必要的功能,所以实现起来相对容易。
在下一期中
武器(3 种类型 x 3 个等级)
我希望实现
我计划稍后将 Unity 项目上传到 GitHub。
请耐心等待。
上传完成后,我会更新文章。
我已将 Unity 项目上传到 GitHub。
希望这能对你有所帮助。
BeBe幸存者
今天的故事就到这里。
*虽然开头写的是“每周”,但下一期计划在三月份上传。
14
