学习创建视频游戏

2025-06-10

学习创建视频游戏

自从我五岁那年圣诞节早上收到我的第一台游戏机——世嘉 Dreamcast 以来,电子游戏就成了我生活中不可或缺的一部分。对我来说,它们是叙事、艺术和科技的终极结合。

刚开始学习编程时,开发电子游戏就成了我最早的尝试之一。可惜的是,那是一种令人沮丧的经历,就像学画画却不知道怎么拿铅笔一样,我的动力很快就消失了。那时,我的编程知识仅限于循环for。尝试了几个星期,却发现几乎没有什么方法行得通,于是我放弃了,感到沮丧和挫败。

一年后,我进入大学学习计算机科学,这激发了我对视频游戏开发之外的编程的热情。

编码和创造力

尽管我是一名工程师,而且属于“科学”一族,但我一直被创造力所驱动。直到13岁左右,我还不确定自己是想成为动画师还是平面设计师,更不敢想象自己会成为一名软件工程师。像编程这样技术性很强的工作,怎么会让人觉得有趣呢?

我意识到,编程很大程度上是一个创造性的过程,它能让我同时满足技术上的和艺术上的渴望。编程满足了我表达创造力的需求,我再也没有想过要重回电子游戏开发领域。

几周前,我在 YouTube 上发现了Sebastian Lague精彩的“编码冒险”系列视频,他在这个系列中探索了热门游戏引擎Unity中的编码概念。我观看的第一个视频就让我着迷。视频中,Sebastian 使用“光线行进”算法生成了令人惊叹的曼德尔球分形:

虽然编程总是充满创造力,但我通常编写的代码输出并不能像上面这样的艺术作品那样唤起同样的感受,也无法唤起你玩最喜欢的电子游戏时的那种快乐。对我来说,最终结果通常是写入数据库的值,或者通过网络发送一些数据。

我总会从这些事情中获得某种满足感,就像我第一次"Hello, World!"在终端窗口中看到这些文字时一样。然而,当你今天写的代码与昨天和前天写的代码在目的上相似时,你的视野就会变得狭窄,你可能会忘记计算机的能力远不止于此。人们很容易忘记代码可以创造出真正美丽的东西;从字面意义上来说,它就是艺术。

制造行星

发现塞巴斯蒂安的系列作品后,我深受启发,决定重新开始游戏开发,并向自己证明,我有能力完成一些曾经让我感到挫败的事情。我下载了 Unity 游戏引擎,在观看了同一位创作者的另一个教程后,决定尝试程序化生成行星表面。

我使用 Unity 提供的脚本 API 构建了一个球形网格(网格是由三角形连接起来的点的集合,这些三角形共同构成一个三维物体)。我们可以为 Unity 提供一组代表世界中点的三维向量(称为顶点),以及一些表示我们希望如何使用三角形连接这些点的整数来实现这一点。使用这些看似简单的工具,我们可以创建任何可以想象到的三维形状。

为了生成星球上的山脉,我将每个顶点的位置传递给梯度噪声函数。梯度噪声在计算机图形学中已经使用多年,用途广泛,它看起来像这样:

对于上图中的每个像素,想象一下,它越亮,像素的高度就越高。因此,我们可以将纯白色区域想象为山峰的顶点,灰色区域代表山腰附近,而最暗的区域代表最低海拔。

现在让我们将这种噪音“包裹”在我们的球体周围,就像用包装纸包裹篮球一样。

如果我们将球体上较亮的点抬高,使其远离圆心,就会发现球体“凹凸不平”。我们可以自定义噪点来增加其密度(通过“缩小”噪点),这样会使球体表面更加粗糙。同样,我们可以放大噪点,使球体表面看起来更光滑。

此时,球体上的噪点分布已经完全均匀。我们“星球”的表面,就像爱丁堡一样,山丘的分布过于均匀。因此,为了生成更逼真的行星表面,我们将噪点分为几层。一层模拟海洋;另一层模拟陆地;第三层形成山丘;最后一层粗糙的噪点将塑造我们星球上山脉的最高峰。

Sebastian 在系列文章中解释了一些额外的小技巧,但最终的成果是一个能够创建相对逼真的行星表面的生成器。下图展示了我调整了一些与噪声应用于球体表面相关的参数后的效果。

最终,我并没有完成这个系列的所有教程。矛盾的是,读完之后我的动力非常高,以至于我不想就此结束整个系列。相反,我想尽快探索尽可能多的 Unity 其他领域。

行星之舞

接下来,我将模拟行星的运动。这次我想让效果更逼真一些,所以我在 Unity Asset Store 上找到了一个太空天空盒。天空盒由六张图片组成,它们以立方体的形式围绕着玩家,让玩家完全被它们包围。在这种情况下,这意味着无论玩家朝哪个方向看,都像是在太空中。

然后,我在 Unity 中创建了一个球体,并将火星表面的图像(来自 Solar System Scope)应用到其中。该图像称为等距矩形投影,这意味着它是通过将火星的球形表面投影到平面上形成的。

我写了一个脚本,把它附加到行星上,让它根据行星的质量对周围的物体施加一个拉力。你懂的,就是重力。我在场景周围放置了一些这样的“行星”,然后在它们周围添加了发光效果,原因很简单,只是我刚发现这是可行的,而且我觉得看起来很酷。

场景布置好后,我坐下来,准备欣赏行星的优雅舞蹈。

事情是这样的……

然后我想起了万有引力的基本定律:苹果从树上掉下来后会落地。苹果不会掉下来后立即开始绕地球旋转。意识到这段舞蹈过于依赖编舞者的物理知识后,我决定开启我的下一个冒险之旅。

交互性

这两个实验都很有趣,让我体会到了使用 Unity 的乐趣,但它们都不是“游戏”。如果没有互动性,它们就只是模拟而已。

尽管这基本上就是一则广告,用来解释为什么NA​​SA永远不应该雇佣我,但我还是很喜欢之前那个场景的“感觉”。我决定保留太空中的元素。这也意味着我不需要为了营造真实感而用素材填充场景。毕竟,太空大部分都是空的。

我想融入一些实时战略 (RTS) 游戏的机制。虽然当时我并没有明确游戏的最终发展方向,但无论最终是否使用,实现有趣的游戏机制都是很棒的体验。我会学习创建这些机制背后的概念,并找出哪些有效,哪些无效。

我首先创建了一个代表宇宙飞船的立方体。然后,我添加了点击立方体来选择它的功能。这涉及到学习射线投射,即向某个方向发射射线,并检查它与什么发生碰撞。为了实现单位选择,我必须从摄像机发出一条射线,穿过光标位置,并检测该射线是否与我的立方体发生碰撞。然后,在选择立方体的情况下,我设计了右键单击,使立方体旋转至正确方向后移动到空间中的某个点。

降低移动速度后,我将立方体升级为一个很酷的飞船资源,该资源是在Unity Asset Store上找到的,并添加了平移、缩放和旋转镜头的功能。我花了将近两个小时才弄清楚如何让飞船在飞行时不再机头朝下。经过一番研究,我发现这是因为 3D 建模软件 Blender 使用的坐标系与 Un​​ity 不同。飞船模型感知的“向前”轴,在 Unity 看来是“向下”。为了解决这个问题,我将飞船模型放置在一个空的 Unity 游戏对象中,调整使其面朝前方(这次是真的面朝前方),并将所有未来的旋转都应用到父对象上,而不是直接应用到飞船上。

我编写了一个小脚本,Rotator通过在每次物理更新时稍微增加旋转来使行星缓慢旋转,这让场景感觉不那么静态。

public class Rotator : MonoBehaviour {
    void FixedUpdate() {
        transform.Rotate(Vector3.up, .01f);
    }
}
Enter fullscreen mode Exit fullscreen mode

框选

大多数自上而下的策略游戏允许你通过拖动矩形框来同时选择多个单位。我运用了从行星生成教程中学到的技巧,在点击和拖动鼠标时动态构建了一个平面。我对生成的网格应用了一个着色器,使其略带色彩,并略微透明。着色器是计算机图形学中的一个概念,它允许你使用 GPU 对对象应用特殊效果,从而提高效率。

着色器通常使用一种特殊的脚本语言编写,学习起来有点困难。然而,在 Unity 中,你可以使用一个名为 Shader Graph 的出色可视化工具来构建着色器,它使用起来非常有趣,我以后肯定会更深入地研究它。

实现这个之后,我意识到在三维空间中进行选择可能有点棘手,尤其是在屏幕上有多个单位并且摄像机旋转的情况下。大脑很难快速理解摄像机当前的旋转角度,也很难知道开始拖动时框的边缘会出现什么角度。

将来我可能会更新我的矩形选择代码,这样你就可以在二维空间中绘制矩形,然后将其投影到游戏世界的三维空间中。这在《星际争霸 2》等游戏中已经实现了,而且感觉更加直观。

目前还不清楚你当前选择了哪些单位。以后我会决定如何将这些信息传达给玩家。这是一个棘手的问题,尤其是在屏幕上可能同时出现多个单位的情况下。

战斗

一款包含太空飞船的游戏显然要求飞船能够相互发射导弹!我编写了一个名为 的脚本BasicAttack,并将其附加到我的飞船预制件上。该脚本定义了诸如“我的攻击能造成多少伤害”之类的内容。最初,攻击是通过射线投射进行的。一条不可见的射线从选定的飞船发射到鼠标点击的方向。如果射线与其他飞船相撞,它会立即从受到伤害的飞船的生命值中扣除攻击飞船的攻击伤害。如果飞船的生命值降至零或更低,它将立即从世界中移除。

由于没有任何视觉反馈,体验相当乏味。所以我又加了一些功能,让体验更生动一些。

我将一个LineRenderer(用于绘制线条,Unity 自带)组件附加到我的飞船预制件 上。Unity 中的预制件类似于编程中的类。它定义了其实例的外观蓝图,并且可以轻松地在任何地方重复使用。线条的起点设置为所选飞船的中心,终点设置为光标的位置。如果用户选择了一艘飞船,并按下A,飞船将进入“攻击模式”,并且LineRenderer将在屏幕上可见。我使用 Shader Graph 创建了一个着色器,并将其应用于线条,使其随着时间的推移缓慢淡入淡出,只是为了让视觉效果更有趣一些。

然后,我更新了BasicAttack代码,生成一枚导弹,而不是发射隐形射线。导弹只是一个胶囊,TrailRenderer上面附有一个(Unity内置的)光斑,当导弹飞向目标时,光斑会跟随导弹。目前,导弹的飞行速度始终是3秒,到达目标需要3秒。3秒后,目标会受到伤害。这不是最现实的方法,但它确实有效。

为了让玩家知道目标受到了多少伤害,我在头疼了好几个小时之后,在目标上方添加了浮动伤害数字。我非常喜欢电子游戏中的这种效果。当目标受到伤害时,其受到的伤害量会短暂地显示在单位上方。字体大小也会根据受到的伤害动态缩放。

在我的实现中,对于 1 到 50 之间的伤害数字,使用标准字体大小(结果是fontScaleFactor1)。当伤害超过 50 时,用于显示伤害数字的字体大小会增大,最大可达 300。如果攻击造成的伤害达到或超过 300,字体大小将是标准大小的两倍。

var fontScaleFactor = 1 + Mathf.InverseLerp(50, 300, damage);
damageText.fontSize *= fontScaleFactor;
Enter fullscreen mode Exit fullscreen mode

由于一艘船可能会连续受到多次伤害,我为这些伤害数字的生成位置添加了一些随机变量。如果没有这种变量,这些数字就会完全重叠,无法确定这艘船受到了多少次伤害。这是我编写的函数,用于将随机性应用于浮动伤害数字的生成位置:

private Vector3 ApplyRandomOffsets(Vector3 vec, float minOffset, float maxOffset) {
    var xOff = Random.Range(minOffset, maxOffset);
    var yOff = Random.Range(minOffset, maxOffset);
    var zOff = Random.Range(minOffset, maxOffset);
    return new Vector3(vec.x + xOff, vec.y + yOff, vec.z + zOff);
}
Enter fullscreen mode Exit fullscreen mode

经过这些改变,事情变得更加视觉上令人兴奋:

爆炸

为了让玩家更好地感受到攻击造成的伤害,我添加了导弹击中目标时触发的爆炸效果。Unity 中的爆炸效果通常使用 Unity 粒子系统来实现,该系统可以创建基于物理的精彩粒子效果。我没有尝试创建自己的纹理和粒子系统,而是使用了Unity 粒子包中提供的一些粒子系统。

我最初尝试让事物爆炸的尝试并没有真正按照计划进行。

经过一番折腾,我升级了那些奇怪的粉色盒子形状,最终得到了一个有点像烟花的形状。虽然没有达到我想要的那种震撼效果,但至少是朝着正确方向迈出了一步。

经过一番谷歌搜索和一些设置调整,我终于意识到问题的根源在于我使用了 Unity 较新的轻量级渲染管线 (LWRP)。爆炸效果所使用的着色器不受支持。将它们更新为 LWRP 支持的着色器后,粒子系统实际上开始看起来像是真实的爆炸效果了:

当导弹击中目标时,目标会产生爆炸效果。为了增加挑战性,我给每艘飞船都配备了“护盾”,相当于额外增加一层生命值。如果导弹击中一艘飞船时,该飞船仍然拥有护盾,爆炸效果会变成蓝色。当飞船的护盾能量耗尽后,爆炸效果会变成橙色,以此来视觉提示玩家该飞船的总生命值已不足。

关于如何改进这里的特效,我还有很多想法。随机改变导弹的打击位置,并让爆炸效果在这些位置生成,可以提升真实感。与其硬编码爆炸效果,让导弹发射3秒后在目标舰船中心出现,不如改用基于碰撞的系统,这样导弹击中目标后就能立即爆炸。蓝色爆炸效果也不是电影或电子游戏中通常呈现的“护盾”效果,所以模拟“力场”效果的着色器可能会更好。

前进

还有很多东西要学,但我对过去几周的进步感到非常满意。虽然这个项目除了“四处飞行和攻击飞船”之外没有真正的游戏玩法,但这仍然是一次非常棒的学习经历,至今仍让我感觉自己完成了学习电子游戏创作的目标。

接下来,我想学习如何为上述项目添加 UI,并可能实现一些有趣的游戏系统。我有一些关于游戏机制的笔记,以及一些想要尝试的想法。我可能还会转向其他项目,比如实现我在其他游戏中体验过的有趣游戏机制,比如《传送门》中的传送枪,或者《战神》中的利维坦之斧

无论如何,学习 Unity 的基础知识为我打开了一个全新的创意世界。现在,我有太多东西想要探索和学习,并且对自己充满信心,相信我一定能够成功学习它们!

将来我可能会创建更多类似的开发/学习日志,讨论我构建的东西以及在这个过程中学到的东西。如果你感兴趣,可以在DEVTwitter上关注我,也可以访问我的个人博客darrenburns.net

鏂囩珷鏉ユ簮锛�https://dev.to/_darrenburns/learning-to-create-video-games-504n
PREV
增强你的命令行,第 3 部分
NEXT
快速入门指南:Elastic Stack for Devs