Replay
前言项目已经传到 github 上了 https://github.com/zong4/SuperHot,3D 版本的会在后续上传。
3D 的版本已经上传好了,推荐阅读 3D 版本的代码,更加优美和完善。
回放的话其实不外乎两种方法,分别对应了服务器的两种状态同步方式:帧同步和状态同步。
帧同步的话就是每一帧都记录所有玩家的输入,然后回放时重新执行一遍。
状态同步的话就是每一帧都记录所有玩家的状态,然后回放时直接把状态设置过去。
因此我们也可以同样的通过记录每一帧各角色的信息或者输入来实现回放,但是由于帧同步就意味着要重新模拟一遍世界,所以我这边现用了实现起来简单的状态同步。
ControllerManager那不管是哪种方法,我们都需要一个 ControllerManager 来管理所有的角色。
我这边由于是状态同步,所以记录信息的任务就统一交给 ControllerManager 来做,在 LateUpdate 中收集所有角色的信息并保存。
其中 Action 中的 Time 是为了在实现 wait 的,从而保证回放时的时间间隔和录制时一致,这里我为了方便才把它放在 Actio ...
Time Manipulate
前言个人认为 SuperHot 的核心是时间控制和回放,一个一个实现。
试了一下发现时间控制实现起来挺简单的,因为 Unity 已经提供了时间尺度(时间流速)的接口,所以只需要包装一下就好了。
代码暂时先不传,等后面的回放做完再一起传吧。
TimeScaleManager由于 UnityEngine.Time.timeScale 是 static 的,所以我这边用单例模式进行管理。
之后如果做成联机,让每个玩家都是独立的话,不知道难度怎么样。
12345678910111213141516171819202122232425262728293031323334353637383940414243public class TimeScaleManager{ // Singleton instance private static TimeScaleManager _instance; // Data private float _timeScale = 1f; public static TimeScaleManager Instance & ...
游戏机制设计师详解
1
构建作品集与在线影响力
在GitHub或ArtStation发布项目代码与设计文档,撰写技术博客分析经典游戏机制(如《塞尔达》的物理交互、《文明》的资源平衡),吸引潜在雇主注意。
21. 理论基础:从设计原则到数学模型
核心概念
推荐学习材料
搜索到的《游戏平衡性设计原则》PPT(网页2)详细拆解了平衡性设计的方法论,如通过玩家反馈、数据分析和A/B测试发现问题。
《游戏设计心得——实现游戏平衡性的技巧》(网页8)提出“模块化设计”和“复杂性控制”原则,例如将游戏要素功能单一化以简化调整流程。
2. 技术工具:数据分析与快速迭代
数据分析工具学习使用游戏引擎内置的Analytics工具(如Unity Analytics)或第三方平台(如GameAnalytics),分析玩家行为数据(如胜率、资源消耗、流失节点),定位平衡问题。
数学建模工具结合计算机背景,用Excel或Python建立平衡性模型。例如,通过马尔可夫链模拟玩家决策路径,或利用博弈论优化多人对战的经济系统。
3. 实践方法:从原型到测试
小型项目实践设计一款简单游戏(如卡牌对战或平台 ...
实现通用的UI布局
前言为什么要实现通用的UI布局?
因为作为对美术一窍不通的程序员,并不想花太多时间去精致的摆放每一个UI,不如直接用锚点,看着太差不差就好了。
但是每次调整Unity锚点时都需要重新设置位置,真的很麻烦,干脆写个脚本,实现在运行时通过滑块调整UI布局。
通用的UI布局何为通用的UI布局?
在我眼里就是网页设计中标准的,页头,正文和页脚三元素(其实就是一分为三块),想要多一点块就不断的嵌套,想要少一点块就将某几块的高度设为0.
于是就可以实现以下这样的PageLayout类。
在Awake时获取三元素的初始数据。
然后在Update时根据滑块数据不断的调整锚点位置。
123456789101112131415161718192021222324252627282930313233343536373839namespace UI{ public class PageLayout : MonoBehaviour { private void Awake() {#if DEBUG _headerR ...
实现设计师友好的代码
设计师友好的代码为什么要开发设计师友好的代码?
好处有很多,可以防止一些意想不到的bug,也可以提高沟通效率……
设置参数反射标记
通过Serializable可以提高参数的可读性,如下图的MoveSettings和AttackSettings。
通过[Min(0f)],[HideInInspector],……,防止设计师乱搞。
12345678910111213141516171819namespace Character{ [Serializable] public class MoveSettings { [Min(0f)] public float moveSpeed; } [Serializable] public class AttackSettings { public GameObject weaponPrefab; public Transform weaponEquippedTransform; [HideInInspecto ...
基于InputSystem实现本地多人游戏(一)
InputSystemInputSystem 是 Unity 自带的一个上层输入系统(隔离了不同的输入设备),可以让我们更方便地处理输入事件,当然同时也不可避免的会失去一些精细的调控。
要想实现本地多人游戏光有 InputSystem 可不够,还需要用 PlayerInput 对输入进行二次绑定以及 PlayerInputManager 来管理多个PlayerInput。
PlayerInput设置
大部分设置保留默认就行。
Camera:这里由于我是 2D 游戏,所以没有设置 Camera。
Behaviour:这里我没有用默认的 SendMessages,是因为我希望能更精准的控制这些 Callback,所以我选择了 InvokeUnityEvents。
脚本需要编写一个脚本来处理所有你需要的输入事件,然后在 PlayerInput 组件上注册函数。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 ...
基于InputSystem实现本地多人游戏(二)
个性化Player相比昨天主要是完整实现了角色的异化,来看看具体的思路吧。
首先,因为 PlayerInputManager 不支持异化,只能构造通用的 Player,所以就没法直接让 PlayerInputManager 根据不同的 PlayerIndex来 实例化不同的 Player。
所以想到可以让 PlayerController 直接管理一个 static list 来存储所有的异化 Player,然后再通过 PlayerIndex 来获取对应的 Player,但是这样破坏了封装性。
干脆就直接让 PlayerController 直接获取对应的 PlayerSpecific,然后拷贝或者创建对应的组件,所以我们就可以在创建中创建一个 PlayersSpecifics 的空物体,然后在里面放置所有的 PlayerSpecific。
再通过下面的代码,为每一个 PlayerIndex 查找对应的 PlayerSpecific,如果找不到就使用默认的 PlayerSpecific。
1234567891011121314151617181920212223242526272 ...
局域网第三人称射击游戏
项目地址
游戏框架
依据作业要求,同时参考 CSGO 以及 PUBG,制定如下框架图。
制作实现思路
单独完成联机大厅以及相关 UI 。
实现人物的动画逻辑以及射击功能。
制作训练营地图,实现单人与训练营的交互。
实现多人与训练营的交互。
制作积分板。
完成对战地图。
制作 AI 。
实现多种武器。
完善动画细节。
移植到安卓。
联机大厅为了方便日后复用这套局域网联机框架,大厅的地图和人物框架都是独立的,在开始游戏后用联机漫游将会话带入游戏地图中。
其中颜色调整(材质替换)是通过将 PlayerIndex 的复制属性设置为 RepNotify 后调用系统生成的广播函数 OnRep_PlayerIndex 。
而玩家列表和聊天记录都是通过生成子 widget 添加进垂直框中实现的。
人物动画大概想了一下整体的动画规划。
首先,整体上分为持枪动作和不持枪动作。
其次可以分别可以做出下蹲、站立、走路、小跑以及快跑和俯仰头。
其中除了快跑和站立和都可以做到八向移动。
最后添加换弹以及死亡动画。
其中俯仰头是通过记录鼠标 Y 轴后推算出俯仰角,再用分层骨骼混合节点实现俯仰头的 ...
基于色彩理论像素化任意图片
代码画像素画观察先看了会大佬的像素作品(32 * 32),感觉像素画主要就是把关键特征保留下来。
动手看了个 b 站的视频,感觉还不错,决定自己也动手临摹一下。
找了张图。
虽然很不甘心,但是我的美术功底好像确实也就到此为止了,实在是太丑了!
图片像素化既然我自己画不行,那就只能让电脑来画了。
纯算法个人还是喜欢用纯算法的方式解决问题(可解释性强)。
改进图像缩放从本质上来说,图像缩放就是去除冗余像素的过程,但是却无法生成一幅赏心悦目的像素画。
其根本原因在于没有突出重要部位的颜色(色差不明显),而是一味的使用均值平滑缩放。
因此,可以先按照缩放比例,将原图像分块,再剔除块内的偏离值,最后取平均值映射到新图像的相应位置。
12345678910111213141516# Every channelfor page in range(0, channel): mean = np.mean(block[:, :, page]) std = np.std(block[:, :, page]) # 剔除 2σ 异常值 numMaxEdge = mean + 1 * ...
一次性布尔多个复杂图形
Bentley Ottmann 扫描线法比较传统的老办法,只能求交不能布尔。
目前最好的文章是这篇。
建议先依据该算法把扫描线的结构搭出来。
Martinez 扫描线法相关论文。
还可以看一下 Boost::polygon 的源码,虽然我没看完,但是感觉用的就是 Martinez 扫描线法(流程很相似),而且它论文上也说它用的是扫描线。
还在网上找到唯一一篇讲解 Martinez 扫描线法的中文文章。
求交求交不需要考虑特殊情况,唯有一些需要人为调控的精度误差,具体思路在论文中已经介绍的很详细了,就不赘述了。
按照 Bentley Ottmann 扫描线法的逻辑将所有点事件加入优先队列中,并依次处理;
点事件排序时,对于坐标值相同的节点事件,应把终点放在起点前面,其次如果都是起点再继续比较另一个端点(终点)的纵坐标(优先)和横坐标。
如果存在线段相交,应将其裁剪并将新的点事件加入优先队列,同时根据布尔规则选取有效线段加入集合中。
布尔因为最近在忙别的任务,暂时只研究了一些。
代码代码讲解。