# 游戏设计

为什么我们需要相机震动

相机震动可以让游戏 feel 更 “好”,具体而言,

  1. 增强反馈:相机震动可以在玩家执行某些动作时提供即时反馈,例如攻击、跳跃或受到伤害。这种反馈可以让玩家更好地理解他们的行为对游戏世界的影响。
  2. 增加沉浸感:通过模拟真实世界中的相机运动,游戏可以让玩家感受到更强的沉浸感。例如,在激烈的战斗场景中,微妙的相机震动可以让玩家感受到紧张和刺激。
  3. 提升游戏节奏:相机震动可以用来强调游戏中的关键时刻,例如击败敌人或完成任务。这种视觉上的强调可以帮助玩家更好地理解游戏节奏和重要事件。

了解震动

震动的类型

游戏中的振动无非是模仿现实中的振动,了解现实中的振动,我们可以更好的设计游戏中的振动。

首先,按照目标来看,震动可以分为以下几种类型:

  1. 位置震动:相机在空间中的位置发生变化。
  2. 旋转震动:相机的朝向发生变化。
  3. 缩放震动:相机的缩放级别发生变化。
  4. 混合震动:同时应用位置、旋转和缩放的震动。

其次,按照震动的特征来看,震动可以分为以下几种类型: 1. 简谐震动:震动的幅度和频率是恒定的。 2. 阻尼震动:震动的幅度随着时间逐渐减小。 3. 各种非线性震动:震动的幅度和频率不是线性关系。

如果按照震动的规律来看,震动可以分为以下几种类型: 1. 简谐振动 2. 周期振动 3. 准周期振动 4. 过渡振动 5. 窄频带随机振动 6. 宽频带随机振动 7. 不平稳随机振动

对于人眼的感知

但是上面的那么多震动类型,对于人眼来说,实际上是比较难以分辨的。比如下面两种震动方式:

这里依次播放了两种震动模式,一个是窄频带随机震动,一个是不平稳随机震动。

这里依次播放了两种震动模式,一个是阻尼震动,一个是弹簧震动,它们在频率分布或者振幅分布上拥有特殊的规律。

人眼对于震动的感知可以分为两个部分: 1. 基于 运动感知 的 瞬时感知 2. 基于 模式识别 的 持续感知

对于瞬时感知,人眼对于震动的频率非常敏感,尤其是高频震动。对于持续感知,人眼更擅长于识别震动的模式和规律。

设计震动

我们 基于上述的震动类型和人眼的感知特点,可以设计出更符合玩家体验的震动效果。

匹配人眼的瞬时感知

查看下面的两种震动方式:

我们可以发现,如果是使用随机偏移生成的震动,它会看起来非常“难受”,这种不适感有两个成因: 1. 瞬时感知困难:是因为对于一个人眼来说,随机偏移将会使得在短时间内,物体发生过大的位移,导致人眼对于运动的感知存在一定的困难。 2. 频率过高:是因为随机偏移的震动频率理论上是无限的(实际和帧率有关),而人眼对于高频震动的感知是非常敏感的。这种高频震动会让人感到不适。

或许你可能会认为还好,但是如果是看下面这个对比(仅仅是将原本的小球替换为了多个点):

阅读全文 »

# 未完成
#

Math for Game Programmers: Juicing Your Cameras With Math

Math for Game Programmers: Juicing Your Cameras With Math

相机运动

相机目标

处理多个玩家

非线性衰减

人眼对于运动的感知并非线性(这和声音类似),我们对于小幅度的震动没有那么敏感,比如下列震动:

The art of screenshake

Jan Willem Nijman - Vlambeer - "The art of screenshake" at INDIGO Classes 2013

"If you have a button that does something in your game, you need to give the player a reason not to press it, otherwise you may as well perform that action automatically all the time" “如果你有一个按钮在你的游戏中做某事,你需要给玩家一个不按下它的理由,否则你还不如一直自动执行那个动作”

Camera Kick

# 游戏体验日记

最近沉迷《绝区零》无法自拔,为了抽到心仪的角色和道具,不得不研究起游戏中最重要的货币——菲林的获取方式。

在尝试了各种充值渠道后,我整理了一份详细的对比数据,包括来源、价格、性价比和抽数等信息,希望能帮大家少走弯路,花最少的钱拿到最多的菲林!


💡 为什么我要研究这个?

相信很多玩家都有过这样的经历:看着商店里动辄几万菲林的礼包,钱包瑟瑟发抖;但又怕错过高性价比的优惠活动。于是我就开始记录自己每一次充值的信息,然后做了个表格,一一对比,看看到底哪个渠道更值得入手。


📊 数据一览(按性价比排序)

下面是我整理出的详细数据表,方便你快速查阅:

菲林数量 来源 价格(元) 性价比(1600菲林/元) 抽数(假设160菲林抽一次)
10030 全套 595 94.92 62.69
11310 大全套 678 95.92 70.69
4880 bt 290 95.08 30.5
5020 dz 289 92.11 31.38
1860 bt 108 92.90 11.63
1860 bt 107 92.04 11.63
3950 dt+dz 221 89.52 24.69
3960 zzz(游戏内直充) 198 80 24.75
6560 zzz 328 80 41
3520 dz 190 86.36 22
3520 dz 175 79.55 22
2950 bt 145 78.64 18.44
3430 dt 180 83.97 21.44
3020 bz 162 85.83 18.88
5150 dt+dz 254 78.91 32.19
5150 dt+dz 250 77.67 32.19
2320 dz 108 74.48 14.5
1920 bt 104 86.67 12
1630 dt 68.88 67.61 10.19
1630 dt 64 62.82 10.19
1630 dt 64 62.82 10.19
1020 bz 40 62.75 6.38
1030 dt 33.88 52.63 6.44
900 dt 24.5 43.56 5.63
720 dz 32.39 71.98 4.5
1000 bz 64 102.4 6.25
160 —— 16 160 1

说明: - bt/dz/dt/bz/zzz 分别代表不同充值渠道: - dt = 抖音投稿 - dz = 直播点赞或任务奖励 - bz = B站直播互动 - zzz = 游戏内直接充值(原石兑换) - bt = 某些特殊活动或合作平台 - 性价比 = 菲林数 ÷ 价格(单位:菲林/元) - 抽数 = 菲林数 ÷ 160(每次抽卡所需菲林)


🔍 渠道解析 & 我的建议

✅ 最推荐:官方充值途径

官方充值渠道是最稳定也最省心的途径,其他的方式要么有的需要对比性价比,要么就是需要找半天。

官方渠道充值首充可以翻倍,也就是说无论如何它的性价比都是 80元/1600菲林,性价比不低。

✅ 日常小额充值:bz 和 dt

以 官方的双倍为基准的话,很多时候一些散的礼包也能拥有非常高的性价比,有的时候比翻倍还多。

但是它们的缺点就是: 1. 每个版本只有一次,如果后续想要继续充值,需要注意不能是已经购买过的途径,否则将会有冲突 2. 数额较少

但是总而言之,性价比还算是不错的,日常也能够花个 60 元左右换一个十连。


当然,每个人的需求和预算都不同,这份表格只是一个参考。希望它能帮你做出更聪明的选择。

# vue
# ts

之前总是会被路径之类的事情所困扰,打包之后的文件路径总是和原本的路径不一样,通过设置路径别名可以让程序在打包的时候自动将路径解析为正确的路径。

同时这种方法还支持自动补全,也就是说,使用这种方式在编写的时候,也不用去基于当前文件填写路径了。

我个人主要是使用 ts + vite 进行开发,下面是这两个语言的配置方法:

TS

tsconfig.json 中添加 paths 配置:

1
2
3
4
5
6
7
8
9
10
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@views/*": ["src/views/*"]
}
}
}

这样的话,在代码中就可以使用 @/ 来代替 src/,例如:

1
2
import MyComponent from '@/components/MyComponent.vue';
import HomeView from '@/views/HomeView.vue';

Vite

vite.config.ts 中添加 resolve.alias 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@assets": path.resolve(__dirname, "./src/assets"),
"@locals": path.resolve(__dirname, "./locals"),
}
}
});

这样在 Vite 中也可以使用 @/ 来代替 src/,例如:

1
2
import MyComponent from '@/components/MyComponent.vue';
import HomeView from '@/views/HomeView.vue';

这样配置之后,你就可以在项目中使用这些别名来简化路径引用了。

阅读全文 »

# vue

下面是分别使用WPF(Windows Presentation Foundation)、React和Vue实现点击按钮后数值加一并在页面上显示变化的简单示例。这将帮助你了解三种技术在处理类似问题时的不同方法。

WPF 示例

首先,确保你已经安装了.NET SDK,并且熟悉XAML和C#语言。

1
2
3
4
5
6
7
8
9
10
<!-- MainWindow.xaml -->
<Window x:Class="CounterApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Counter" Height="200" Width="300">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding CounterValue}" FontSize="48"/>
<Button Content="Increment" Command="{Binding IncrementCommand}" Width="150" Height="50"/>
</StackPanel>
</Window>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// MainWindow.xaml.cs
using System.Windows;
using System.Windows.Input;

namespace CounterApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}

public class MainViewModel : BaseViewModel
{
private int _counterValue = 0;

public int CounterValue
{
get => _counterValue;
set
{
_counterValue = value;
OnPropertyChanged(nameof(CounterValue));
}
}

public ICommand IncrementCommand { get; }

public MainViewModel()
{
IncrementCommand = new RelayCommand(Increment);
}

private void Increment(object obj)
{
CounterValue++;
}
}
}

这里我们使用了MVVM模式,MainViewModel作为视图模型管理数据和命令逻辑。

React 示例

接下来是React版本,使用函数组件和Hooks。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);

return (
<div style={{textAlign: 'center', marginTop: '50px'}}>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

export default Counter;

这个例子非常直观,利用了useState Hook来管理状态。

Vue 示例

最后是Vue版本,同样简洁明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div style="text-align: center; margin-top: 50px;">
<h1>{{ count }}</h1>
<button @click="increment">Increment</button>
</div>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);

function increment() {
count.value++;
}
</script>

在这个Vue的例子中,我们使用data选项定义了一个响应式属性count,并用methods选项添加了一个增加计数的方法。

总结

  • WPF: 使用MVVM设计模式,通过绑定机制将UI与逻辑分离,适合构建复杂的桌面应用程序。
  • React: 基于组件和状态管理,使用JSX语法混合HTML和JavaScript代码,非常适合Web应用开发。
  • Vue: 结合模板和脚本部分,易于理解和学习,也提供了灵活的状态管理方案,适用于快速Web开发。

每个框架或平台都有其独特的优势,选择哪一个取决于项目需求和个人偏好。

# 编程

# 杂项技术

# 未完成

之前的那个教程已经失效了,因为frxy在大概去年的9月份(小王牌)上线后,更换了新版的npk加密模式,所以说需要重新判断以及处理文件。

本人也是第一次手动处理这一部分,如果有缺漏的话请多包含~

文件提取

这一部分我觉得应该不用过多介绍,在手机内下载好frxy,并且进入游戏,下载好必要资源之后,你能够在

1
Android\data\com.netease.frxy.huawei

内找到 frxy 的所有文件。

这里面文件挺多的,关于文件结构的描述上一篇文章有说:
[[网易游戏解包尝试]]

这里就不赘述了。这次我们想要解包出模型文件,模型文件位于:com.netease.frxy.huawei\files\netease\frxy\Documents\res_nip\char\charselect 下。

模式判断

首先我们需要尝试识别 npk 文件的格式

通过观察和对比几个npk文件,我们可以发现它们拥有相似的开头:

1
2
3
4
5
6
7
8
00069.npk: 4e58504b 1a010000 00000000 00000000 02000000 e8cd4101 28b52ffd 60a0726d 1500c664 8e4bb0d4 5507e3dc d031b765
00070.npk: 4e58504b 19000000 00000000 00000000 02000000 f0530c00 28b52ffd 60fa3305 93002af1 f42d4510 486d670e c0950800
00072.npk: 4e58504b 29000000 00000000 00000000 02000000 885c1a00 28b52ffd a03be206 00947f0e 0ecc1b8e 6e211028 dd18afd2
00074.npk: 4e58504b 1b000000 00000000 00000000 02000000 14e61700 28b52ffd 603e04fd 10007657 4d27e0ce 58076bb6 1100b7e6
00116.npk: 4e58504b 27010000 00000000 00000000 02000000 84026901 28b52ffd 60a0723d fa005a88 b15e4310 20acd518 248632a1
00129.npk: 4e58504b f2000000 00000000 00000000 02000000 b04b2501 28b52ffd a050ce01 00bd8e04 1ed34885 22341060 db035810
00130.npk: 4e58504b e0000000 00000000 00000000 02000000 48983d01 28b52ffd 60fc557d 3400c674 a126f090 ea01fb43 46412b17
00150.npk: 4e58504b 15000000 00000000 00000000 02000000 48781900 28b52ffd 60160665 180066f6 7a1c5057 a103db19 55e67677

可以发现,它们的开头的格式基本上一样,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
000070:
13aba15c 0c0c 01 000200 000200 010000
13aba15c 0c0c 01 000400 000400 010000
13aba15c 0c0c 01 000400 000400 010000
13aba15c 0c0c 01 800000 800000 010000
13aba15c 0c0c 01 000400 000400 010000
13aba15c 0c0c 01 800000 800000 010000

000072:
13aba15c 0c0c 01 000100 000100 010000
13aba15c 0c0c 01 000400 000400 010000
13aba15c 0c0c 01 000100 000100 010000
13aba15c 0c0c 01 000400 000400 010000
13aba15c 0c0c 01 000400 000400 010000
阅读全文 »

# 效率工具

有的时候玩一些游戏的时候会感觉赶路或者动画非常无聊,想要加速(比如《纸间密隙》的走路就慢的一批,还有比如《少前2》的战棋播动画,虽然官方给了最高3倍速,但是还是很慢)。

但是使用 CE 修改器的加速精灵却失效了,在网上找到了解决办法,在这里记录一下:

解决办法

下面几个依次尝试就好了,一般第一个就能解决问题:

关闭垂直同步,在游戏设置中关闭垂直同步选项。 关闭全屏模式,在游戏设置中将游戏改为窗口模式。 关闭V-Sync,在游戏设置中关闭V-Sync选项。 关闭帧生成功能/DLSS,在游戏设置中关闭帧生成功能或DLSS选项。

为什么是垂直同步

垂直同步(V-Sync)是一种图形渲染技术,用于防止画面撕裂和卡顿现象。它通过将游戏的帧率限制在显示器的刷新率范围内来实现这一点。 当垂直同步开启时,游戏的帧率会被锁定在显示器的刷新率(通常是60Hz或144Hz)上,这意味着游戏的帧率不会超过显示器的刷新率。

因此,当CE修改器的加速精灵试图加速游戏时,游戏的帧率可能会被锁定在显示器的刷新率上,从而导致加速效果失效。

阅读全文 »

关卡1-1 我 无 (看到半散的织机)这是...? 关卡1-2 农民B 失望 今早发现几个部件掉落了... 关卡1-3 农民B 失望 老朽试着装回去,却总不对劲... 关卡1-4 我 思考 让在下看看...(观察散落部件) 关卡1-5 脑海声音 无 "此织机之理,重在力之传导..." 关卡1-6 我 惊讶 !? 关卡1-7 黄道娘 无 (虚影显现)"老身黄氏..." 关卡1-8 黄道娘 思考 "织机运作,如流水行云..." 关卡1-9 黄道娘 无 "踏力传至经线,引梭穿纬..." 关卡1-10 黄道娘 无 "各部件相承,环环相扣..." 关卡1-11 黄道娘 思考 "观其形,思其势..." 关卡1-12 我 开心 原来如此! 关卡1-13 我 思考 (走向织机)让我试试...

帮我优化一下文案,要求如下: 1. 表情只能在下面几个之间选择:失望/思考/无/开心/不满/惊讶/痛苦 2. 不能够出现未出现过的人物 3. 对话需要注意人物形象 4. 可以增加对话数量 5. 以表格形式给我 6. 对话内容需要有逻辑性,不能出现不符合逻辑的对话 7. 主角使用现代风格,其他角色使用偏文言白话的风格 8. 在脑内发生的对话使用""括起来,其他对话则不需要括起来

请按照以下步骤,一步步来: 1. 分析对话核心内容 2. 尝试分析环境背景,补充背景细节,考虑人物处境 3. 分析人物形象,推测他们对话风格,性格等 4. 优化文案

0%