From 2f7f66f41ea82d5519304317fd86387dc09f10f1 Mon Sep 17 00:00:00 2001 From: littlesulley <494597918@qq.com> Date: Fri, 28 Jul 2023 20:05:54 +0800 Subject: [PATCH] Site updated: 2023-07-28 20:05:28 --- 2022/03/27/08/38/index.html | 2 +- 2022/10/22/17/37/index.html | 2 +- about/index.html | 18 +- index.html | 11 +- page/2/index.html | 11 +- page/3/index.html | 11 +- page/4/index.html | 11 +- photos/index.html | 2 +- search.xml | 8556 +++++++++++++++++------------------ sitemap.txt | 16 +- sitemap.xml | 160 +- 11 files changed, 4418 insertions(+), 4382 deletions(-) diff --git a/2022/03/27/08/38/index.html b/2022/03/27/08/38/index.html index 6f92cf8d..480f0750 100644 --- a/2022/03/27/08/38/index.html +++ b/2022/03/27/08/38/index.html @@ -21,8 +21,8 @@ - + diff --git a/2022/10/22/17/37/index.html b/2022/10/22/17/37/index.html index 3b33726c..5729d711 100644 --- a/2022/10/22/17/37/index.html +++ b/2022/10/22/17/37/index.html @@ -59,9 +59,9 @@ - + diff --git a/about/index.html b/about/index.html index eca13fa0..4a010cd6 100644 --- a/about/index.html +++ b/about/index.html @@ -6,15 +6,15 @@ - + - + - + @@ -110,12 +110,12 @@

Sulley

-

I received my bachelor's degree from -Peking University in 2020, where I study computational linguistics and -also take some math as well as computer science courses. My interests -are to advance state-of-the-art video game techniques and apply deep -learning based AI algorithms to gameplays.
- I received my bachelor's degree from Peking University in 2020, where +I study computational linguistics and also take some math as well as +computer science courses. My interests are to advance state-of-the-art +video game techniques and apply deep learning based AI algorithms to +gameplays.

+

@Sulley
Sulley and + --->, @@ -250,6 +250,15 @@

Sulley

and + + + + + + . diff --git a/page/2/index.html b/page/2/index.html index 64ed6ddf..7e285fe1 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -241,7 +241,7 @@

Sulley

and + --->, @@ -250,6 +250,15 @@

Sulley

and + + + + + + . diff --git a/page/3/index.html b/page/3/index.html index da18a28c..1b8a0982 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -241,7 +241,7 @@

Sulley

and + --->, @@ -250,6 +250,15 @@

Sulley

and + + + + + + . diff --git a/page/4/index.html b/page/4/index.html index f5ac29ec..fab7ac97 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -241,7 +241,7 @@

Sulley

and + --->, @@ -250,6 +250,15 @@

Sulley

and + + + + + + . diff --git a/photos/index.html b/photos/index.html index 1555f49d..73108052 100644 --- a/photos/index.html +++ b/photos/index.html @@ -12,7 +12,7 @@ - + diff --git a/search.xml b/search.xml index 58a6ebf3..7702ba70 100644 --- a/search.xml +++ b/search.xml @@ -617,6 +617,63 @@ https://dl.acm.org/doi/pdf/10.1145/198429.198435

蓄水池采样 + + Bresenham算法画直线 + /2020/10/18/16/03/ + Bresenham's +Algorithm是计算机图形学中用于画直线的算法,它只涉及整型数值的乘法和加法,在底层运算上提高了效率。本文介绍Bresenham算法的思路与扩展。

+ +

我们用显示器看到的各种图像都是由一个一个像素组成的,比如计算机要在显示器上显示一条线段,那么就要决定哪些像素填色,哪些不填色,如下图所示。下面我们假定像素点坐标为且坐标为整数,所给线段的两端点为,且坐标也为整数。

+

+

Naive Algorithm

+

显然,线段所在的直线的斜率是,直线的方程就能够显式地写出来:,其中

+

于是我们可以得到一个非常朴素的算法:从点开始,每次让横坐标加一,即,然后依次把横坐标代入到直线方程中,得到纵坐标,然后去找离它最近的,可以表达为,从而点就是要填色的像素。接下来考虑,重复这个过程,直到

+

这个方法如下图所示,注意红色线段和蓝色线段部分是直线上的变化:

+
+给定线段的两点,可以得到它的直线方程,然后依次增大像素点的横坐标,求出对应横坐标的函数值,判断它的纵坐标是什么,得到填充点 + +
+

当然这个算法可以再高效一点,因为上面在算的时候是用直线方程去算,每次计算都有一个加法和一个乘法,我们可以迭代地去做:。现在,计算就只需要一次加法即可。

+

这个算法的好处在于,只要直线不是,无论是多少,两个点的位置如何,这个算法都适用。当然,的情况特殊考虑即可,问题不大。

+

但是这个算法的问题在于,斜率往往是浮点数(即使端点都是整型),而计算机计算浮点数的效率远没有计算整型快,而在图形学中,需要进行大量的即时演算,我们希望能够尽量避免浮点运算。而且,在求时必不可少需要取整,这进一步降低了运算的效率。

+

Bresenham Algorithm

+

下面我们介绍Bresenham算法,一种基于Naive算法的改进版本,它不需要使用取整函数,而且大部分的加法与乘法运算都是基于整型,在运算效率上大大提高。下面我们假设直线斜率,原因及一般的情况我们在下面会介绍。

+

现在我们仔细观察Naive算法,会发现其计算瓶颈主要由带来,换句话说,Naive算法假定我们对每一个都要去计算它在直线上的精确位置,而且还要直接计算它离哪个纵坐标更近。

+

假定我们现在已经着色了点,如下图所示。

+
+Bresenhan算法相比Naive算法的改进之处在于,在求像素纵坐标的时候,不直接用取整函数,而是考虑上下相邻两个像素点与函数值的距离,小的那个才是真正需要着色的,于是就把问题转为了两段距离的大小比较 + +
+

由于我们的假定,所以下一个要着色的点只能是或者,也就等价于计算的大小,我们把它们分别记为。现在对它们做差,就得到:

+

+

上面,是与无关的常量。所以,现在我们就只关心的正负如何,但是上式还是有,我们记,于是,代入到上式,就得到了:

+

+

由于我们又假设了,所以为正,要判断的符号也就是要判断的符号,此刻我们记。所以,只要判断是大于0还是小于0,就可以判断对横坐标而言,是要选还是为纵坐标了。

+

现在我们把代入,就得到初始值:

+

虽然上式已经成功去掉了计算,但是要计算还需要每次都加常数项,而它仍然包含了浮点数,我们还是想要不计算它们。但是我们观察到,都包含常数项,所以我们对它们做差就可以消去常数项,得到:

+

+

或者写成下面的递推形式:

+

+

现在,已经不用再计算浮点数了。我们前面已经说了,可以用去判断的关系,然后,就可以把代入上式,计算出了。

+

所以,当的时候,等于,此时计算得;当时,,此时计算得。然后再用去判断,从而可以计算,一直下去,直到

+

注意我们的两个前提,前者保证了每次最多增加1,后者保证了为正。

+

总结来说,Bresenham算法的流程是: 这里的根据计算结果选择。

+

下面是一个例子,设给定的两点是,从而,把这些都计算好之后,首先填充这个像素,然后发现,则下一个点的纵坐标还是3,于是填色像素,计算;发现又为正,则下一个像素是,计算;此时发现为负,则向上移动一格,要填充的像素是,再计算;以此类推。

+
+一个使用Bresenhan算法画线的实例 + +
+

由于线段没有方向性,所以我们可以始终将第一个点设为第二个点的左侧,即。那么现在的问题就是要解决的问题了。可以发现,不同的其实对应了平面的不同区域,根据对角线与坐标轴可以分为四个区域。现在,只需要对对应的线段进行对称操作,就可以转换为的情况。在求得所有填色的像素之后,再把这些像素对称回去即可。

+]]>
+ + 数学 - 图形学 + + + 随笔 + 计算机 + 算法 + +
Bump Mapping, Normal Mapping and Displacement Mapping /2022/10/12/18/39/ @@ -736,63 +793,6 @@ CN法线贴图
游戏
- - Bresenham算法画直线 - /2020/10/18/16/03/ - Bresenham's -Algorithm是计算机图形学中用于画直线的算法,它只涉及整型数值的乘法和加法,在底层运算上提高了效率。本文介绍Bresenham算法的思路与扩展。

- -

我们用显示器看到的各种图像都是由一个一个像素组成的,比如计算机要在显示器上显示一条线段,那么就要决定哪些像素填色,哪些不填色,如下图所示。下面我们假定像素点坐标为且坐标为整数,所给线段的两端点为,且坐标也为整数。

-

-

Naive Algorithm

-

显然,线段所在的直线的斜率是,直线的方程就能够显式地写出来:,其中

-

于是我们可以得到一个非常朴素的算法:从点开始,每次让横坐标加一,即,然后依次把横坐标代入到直线方程中,得到纵坐标,然后去找离它最近的,可以表达为,从而点就是要填色的像素。接下来考虑,重复这个过程,直到

-

这个方法如下图所示,注意红色线段和蓝色线段部分是直线上的变化:

-
-给定线段的两点,可以得到它的直线方程,然后依次增大像素点的横坐标,求出对应横坐标的函数值,判断它的纵坐标是什么,得到填充点 - -
-

当然这个算法可以再高效一点,因为上面在算的时候是用直线方程去算,每次计算都有一个加法和一个乘法,我们可以迭代地去做:。现在,计算就只需要一次加法即可。

-

这个算法的好处在于,只要直线不是,无论是多少,两个点的位置如何,这个算法都适用。当然,的情况特殊考虑即可,问题不大。

-

但是这个算法的问题在于,斜率往往是浮点数(即使端点都是整型),而计算机计算浮点数的效率远没有计算整型快,而在图形学中,需要进行大量的即时演算,我们希望能够尽量避免浮点运算。而且,在求时必不可少需要取整,这进一步降低了运算的效率。

-

Bresenham Algorithm

-

下面我们介绍Bresenham算法,一种基于Naive算法的改进版本,它不需要使用取整函数,而且大部分的加法与乘法运算都是基于整型,在运算效率上大大提高。下面我们假设直线斜率,原因及一般的情况我们在下面会介绍。

-

现在我们仔细观察Naive算法,会发现其计算瓶颈主要由带来,换句话说,Naive算法假定我们对每一个都要去计算它在直线上的精确位置,而且还要直接计算它离哪个纵坐标更近。

-

假定我们现在已经着色了点,如下图所示。

-
-Bresenhan算法相比Naive算法的改进之处在于,在求像素纵坐标的时候,不直接用取整函数,而是考虑上下相邻两个像素点与函数值的距离,小的那个才是真正需要着色的,于是就把问题转为了两段距离的大小比较 - -
-

由于我们的假定,所以下一个要着色的点只能是或者,也就等价于计算的大小,我们把它们分别记为。现在对它们做差,就得到:

-

-

上面,是与无关的常量。所以,现在我们就只关心的正负如何,但是上式还是有,我们记,于是,代入到上式,就得到了:

-

-

由于我们又假设了,所以为正,要判断的符号也就是要判断的符号,此刻我们记。所以,只要判断是大于0还是小于0,就可以判断对横坐标而言,是要选还是为纵坐标了。

-

现在我们把代入,就得到初始值:

-

虽然上式已经成功去掉了计算,但是要计算还需要每次都加常数项,而它仍然包含了浮点数,我们还是想要不计算它们。但是我们观察到,都包含常数项,所以我们对它们做差就可以消去常数项,得到:

-

-

或者写成下面的递推形式:

-

-

现在,已经不用再计算浮点数了。我们前面已经说了,可以用去判断的关系,然后,就可以把代入上式,计算出了。

-

所以,当的时候,等于,此时计算得;当时,,此时计算得。然后再用去判断,从而可以计算,一直下去,直到

-

注意我们的两个前提,前者保证了每次最多增加1,后者保证了为正。

-

总结来说,Bresenham算法的流程是: 这里的根据计算结果选择。

-

下面是一个例子,设给定的两点是,从而,把这些都计算好之后,首先填充这个像素,然后发现,则下一个点的纵坐标还是3,于是填色像素,计算;发现又为正,则下一个像素是,计算;此时发现为负,则向上移动一格,要填充的像素是,再计算;以此类推。

-
-一个使用Bresenhan算法画线的实例 - -
-

由于线段没有方向性,所以我们可以始终将第一个点设为第二个点的左侧,即。那么现在的问题就是要解决的问题了。可以发现,不同的其实对应了平面的不同区域,根据对角线与坐标轴可以分为四个区域。现在,只需要对对应的线段进行对称操作,就可以转换为的情况。在求得所有填色的像素之后,再把这些像素对称回去即可。

-]]>
- - 数学 - 图形学 - - - 随笔 - 计算机 - 算法 - -
Customizing Actor's Details Panel and Accessing its CDO Components /2023/03/02/10/53/ @@ -1638,2008 +1638,1464 @@ Verlet and adding Vetlet damping - The Jittering Issue with Damping in Cinemachine and How to Tackle it - /2023/07/08/18/22/ - If you are familiar with Cinemachine. you probably know there is a -knotty problem with Cinemachine' damping if you are using -Framing Transposer or some other components to track a -follow point. That is, the camera jitters with damping enabled under -unstable frame rate. The more unstable frame rate is, the more heavily -camera will jitter. This post will discuss this phenomenon and proposes -a workaround to solve this issue.

+ Midjourney和NovelAI不完全使用指南 + /2022/10/22/17/37/ + 显然,AI绘画已经发展到了一个新的阶段。尽管现在市面上已经有不少AI绘画工具,但要熟练地掌握使用它们也绝非易事。本文从实践出发,为读者讲解如何更好地使用Midjourney和NovelAI这两个时下火热的AI绘画工具,但由于笔者使用时间较短,无法完全驾驭AI绘画工具,因此本文是“不完全指南”。如果本文有任意谬误或缺漏,希望读者能不吝指出。

-

Camera jitters with -damping in Cinemachine

-

Unity's Cinemachine has a notoriously severe problem that may cause -the follow object to seemingly jitter when you are using the -Framing Transposer component with damping enabled.

-

To show this, I did a simple experiment. I created a new blank scene -and spawned a new attached with the following script:

-
public class CubeMove : MonoBehaviour
{
public float speed;
public int fps;

public float CurrentSpeed
{
get { return currentSpeed; }
}

private float elapsedTime = 0.0f;
private float currentSpeed;

void Start()
{
if (fps != 0)
{
Application.targetFrameRate = fps;
}

currentSpeed = speed;
}

void Update()
{
elapsedTime += Time.deltaTime;

if (elapsedTime >= 5.0f)
{
if (currentSpeed > 0.0f)
{
currentSpeed = 0.0f;
}
else
{
currentSpeed = speed;
}

elapsedTime = 0.0f;
}

transform.position += new Vector3(1, 0, 0) * currentSpeed * Time.deltaTime;

}
}
-

This script moves the cube for 5 seconds and then keeps it steady for -another 5 seconds and continues moving. The move speed as -well as the fps (frames per second) can be set for test -under different conditions.

-

A new virtual camera is then created with a -Framing Transposer component following this cube. A default -damping of 0.2 is used.

-

Here is result with speed is 100 and fps is -0 (when set to 0, the real fps is determined by Unity, may but -unstable).

-

-

The jitters are very clear. You can also notice that the frame rate -(presented in the Statistics panel) is very unstable, and we will know -soon it is the unstable fps that results in camera jitters.

-

Cinemachine proposes a workaround to alleviate this problem, that is, -to use the revised version of damping where they sub-divide each frame -and simulates damping in the consecutive series of sub-frames. To enable -this functionality, go to Edit -> Project Settings -> Player -> -Script Compilation and add the -CINEMACHINE_EXPERIMENTAL_DAMPING marco to it.

-

-

OKay, now we have enabled the new damping algorithm and let's see how -it will mitigate the jittering issue. Here is result with the same -setting we used in our previous experiment, i.e., speed is -100 and fps is 0.

-

-

It is astonishing to see the jittering issue becomes even more -severe. I conjecture that the variance of fps will significantly amplify -camera jitters when this feature is enabled. In other words, the -experimental damping algorithm responds to the variance of fps in a -NON-linear way: when the variance is small, the experiment damping will -reduce the gaps of camera location between contiguous frames; but when -the variance is large, it will enlarge the gaps, leading to unacceptable -jittering. (Note: I did not validate this conjecture. If you are -interested, just review the code and test it yourself.)

-

What about the expected result if fps is stable? Let's take more -experiments!

-

Here is result with speed is 100 and fps is -120 (very high fps, which is usually prohibitive in shipped games).

-

-

Very steady camera! What about setting fps to 60? Here -is the result.

-

-

An fps of 60 performs equally well with 120, which is anticipated as -fps is stable. Okay, let's try a final experiment where fps is set at an -extreme value of 20.

-

-

Even a low fps of 20 makes our camera stable, only if fps itself is -stable.

-

Now we can conclude that it is the instability of fps that induces -camera jitters, regardless of the exact value of fps. But, why?

-

Why camera jitters

-

Before answering this question, let us first take a look at the -source of damping implemented in Cinemachine.

-
public static float Damp(float initial, float dampTime, float deltaTime)
{
if (dampTime < Epsilon || Mathf.Abs(initial) < Epsilon)
return initial;
if (deltaTime < Epsilon)
return 0;

public const float kNegligibleResidual = 0.01f;
const float kLogNegligibleResidual = -4.605170186f; // == math.Log(kNegligibleResidual=0.01f);
float k = -kLogNegligibleResidual / dampTime; //DecayConstant(dampTime, kNegligibleResidual);

#if CINEMACHINE_EXPERIMENTAL_DAMPING
// Try to reduce damage caused by frametime variability
float step = Time.fixedDeltaTime;
if (deltaTime != step)
step /= 5;
int numSteps = Mathf.FloorToInt(deltaTime / step);
float vel = initial * step / deltaTime;
float decayConstant = Mathf.Exp(-k * step);
float r = 0;
for (int i = 0; i < numSteps; ++i)
r = (r + vel) * decayConstant;
float d = deltaTime - (step * numSteps);
if (d > Epsilon)
r = Mathf.Lerp(r, (r + vel) * decayConstant, d / step);
return initial - r;
#else
return initial * (1 - Mathf.Exp(-k * deltaTime));
#endif
}
-

Translating into mathematics, we have:

-

-

where is the damp time -parameter and the elapsed -time in this frame. This equation decays the input , the distance for the -camera to go to the desired position, by an exponential factor . If , the residual will be , meaning that at -this frame, the camera will traverse 99% of the desired distance to go, -only remaining 1% amount for future frames.

-

OK, let's assume we've placed a cube in the origin and it moves along -the x-axis at a fixed speed, say, -m/s. A camera is placed to track the cube with damping where damp time -. Let's further denote the -delta time for each frame by , where is the -th -frame.

-

Having all variables fully prepared, we can then simulate the object -movement and camera track process.

-

In the beginning of 0-th frame, the camera and the cube are both at -the origin, i.e., (0, 0, 0). As the cube only moves along x-axis, we can -emit the y and z dimensions and use a one-dimensional coordiante to -represent cube and camera positions.

-

At the 1-th frame, the cube moves to , the -distance the camera traverses is , and the residual is . We set for simplicity.

-

At the 2-th frame, the cube moves to , the distance the camera traverses is -, and the residual is .

-

At the k-th frame, we have , -, and -.

-

Without loss of generality, we can set . The following sections will use this -settings unless otherwise stated.

-

For different combinations of , may have different results. Let's dive into and see how it influences -the results.

-

Case 1: Stable FPS, -all are equal

-

When all are -equal, say , our equations -reduce to:

-

-

apparently has a -limitation of when -since . This -explains why a camera with damping always has a maximum distance to its -following target. There maximum distance, also the supremum, is exactly -. When is larger, will be larger, implying the -maximum distance between the camera and its following target will be -larger.

-

What if is -mutable? In this case, we can assume there exists an upper bound such that all satisty . Then we are -able to derive the same conclusion.

-

Another question is, why camera does not jitter when FPS is stable? -We turn to examine the sign of :

-

-

Therefore, when FPS is stable, is always larger than , and jitter will never -happen.

-

Case 2: Unstable FPS, vary

-

When FPS is unstable, where may mutate, how will the camera move in response to its -following target? We can still examine the sign of , but in another -way:

-

-

This equation uncovers why camera jitters happen with unstable FPS. -The residual at the k-th frame is essentially an interpolation -between the following target's current position increment and the last frame's -negative residual , where -the interpolation strength is the decaying factor . -As both and are fixed, a change in will incline the resulting -residual to different ends, -either or .

-

In our simplified case in which the target moves at a fixed speed in -the direction of x-axis, will always be positive (though its magnitude can vary) and - will always be negative. -A mutating thus has -a chance to alter the sign of , which further brings -about camera jitters.

-

So when will camera jitter? From the above equation, we know that -camera will jitter when the sign of consistently changes -over time, i.e., the value of oscillates around zero. -Let's make it equal to zero and see what we can find then.

-

-

This equation tells us when is near , camera will have a large chance to jitter. This -motivates us to improve damping by filtering out the occasions where - is very close to -.

-

What about going deeper? We can treat as variable, and all other -as constants. This abstraction gives us a function of :

-

-

Taking the derivative of , -we know that is monotonically -decreasing when and monotonically increasing when , and . Hence, to make the sign of mutable, must be positive and the -minimum of must be -negative.

-

The minimum of can be -easily computed:

-

-

The last inequality holds because .

-

-

This reveals the fact that: when , a variant is likely to cause to change its sign, -thus resulting in camera jitters. Suppose is large enough, so then -the k-th residual gets -smaller than while is positive. A smaller pushes to become smaller for the next frame, -which further pushes the root of the function to become larger. In this -case, even with the same delta time, will have a larger chance wo -fall in the negative area, i.e., is more likely to be less than the root.

-

-

Solutions

-

Solution 1: imposing an -invalid range

-

Based on what we've discussed so far, we can immediately come up with -a simple solution: enforce to be if they are very close. That is to say, we use a -small value , if , we just set to .

-

Note that can be zero or negative. If this is the case, we keep the -original without -doing anything. Besides, you should be aware that here is not the time this -frame actually takes, instead, it is just the duration used to calculate -damping.

-

Let us explain it more quantitatively. Suppose , -where . -Then according to our algorithm. We then -plug into the original -expression of :

-

-

This demonstrates that now the camera lags behind its following -target more than the previous frame since the residual is larger. After -substituting -with , would be zero, meaning -that the camera now keeps the same frame as last frame. Camera does not -jitter.

-

Here comes the question: what if the following target slows down, or -stops, or even turns back to the opposite direction and the camera still -remains the same residual to it?

-

It is quite a good question. But if we look carefully at the function -of , we will find this -situation will never happen. Let's rewrite here:

-

-

This time, we do not constrain the value of , but at last frame, it's positive.

-

When gets smaller but still -positive, we observe the function gradually shifts leftwards, pushing -the root towards zero. This implies that the area gets -contracted and the probability of remaining the same residual gets -smaller.

-

-

When is zero where the -following target stops, the current residual can be readily calculated -as , which closes the distance gap between the camera -of the following target. The ratio, which is calculated as , would be -devided by zero, outputting an infinite value.

-

When is negative, will be negative. The -ratio -now becomes negative, also beyond the range of .

-

We can implement this algorithm in less than 100 lines of code. You -should modify three files in the official Cinemachine source code -directory.

-

First is Predictor.cs. Add a ImprovedDamp -function:

-
public static float ImprovedDamp(float initial, float dampTime, float deltaTime, float bonus)
{

if (dampTime < Epsilon || Mathf.Abs(initial) < Epsilon)
return initial;
if (deltaTime < Epsilon)
return 0;

float tolerance = 0.05f;

float alpha = Mathf.Log(bonus) * dampTime / kLogNegligibleResidual;
float ratio = deltaTime / alpha;

if (ratio <= 1.0f + tolerance && ratio >= 1.0f - tolerance)
{
deltaTime = alpha;
}

float k = -kLogNegligibleResidual / dampTime; //DecayConstant(dampTime, kNegligibleResidual);

return initial * (1 - Mathf.Exp(-k * deltaTime));
}
-

The input bonus is . Parameter tolerance is what you should set -as we've introduced -above.

-

In file CinemachineVirtualCameraBase.cs, add a new -function ImprovedDetachedFollowTargetDamp:

-
public Vector3 ImprovedDetachedFollowTargetDamp(Vector3 initial, Vector3 dampTime, float deltaTime)
{
GameObject go = GameObject.Find("Cube"); // Hard find our following target of interest, you should not do like this!
Vector3 deltaDistance = new Vector3(100, 0, 0) * deltaTime; // Hard set the velocity, you should not do like this!
Vector3 residual = initial - deltaDistance;
Vector3 bonus =
new Vector3(residual.x / (residual.x + deltaDistance.x + 1e-7f),
residual.y / (residual.y + deltaDistance.y + 1e-7f),
residual.z / (residual.z + deltaDistance.z + 1e-7f));

dampTime = Vector3.Lerp(Vector3.Max(Vector3.one, dampTime), dampTime, FollowTargetAttachment);
deltaTime = Mathf.Lerp(0, deltaTime, FollowTargetAttachment);
return Damper.ImprovedDamp(initial, dampTime, deltaTime, bonus);
}
-

This piece of code is very informal, and you should never write your -code like this. The purpose of this function is to get and . I reckon the correct -way to do this is to create a new (or two) variable in the -CinemachineVirtualCameraBase class and update it in each -tick. The code presented here is only for demonstration.

-

In file CinemachineFramingTransposer.cs, change the -called function for damping:

-
cameraOffset = VirtualCamera.ImprovedDetachedFollowTargetDamp(  // Original is DetachedFollowTargetDamp
cameraOffset, new Vector3(m_XDamping, m_YDamping, m_ZDamping), deltaTime);
-

You could also try other components, not just -FramingTransposer here.

-

With the default tolerance=0.05, the result is shown -below.

-

-

Camera jitters disappear. Note that the general fps is quite high -(around 400~500). This is because our scene is quite simple, containing -only a cube and a camera. In order to simulate a more real runtime game -situation, I place 20k cubes in the scene and now the fps is around 30, -but still unstable.

-

Below is the result when using the raw damping algorithm.

-

-

Camera jitters more severely due to a generally lower FPS. What about -using the improved damping algorithm? Here is result with -tolerance=0.05.

-

-

Just as expected, camera jitters do not show up. Let's try different -tolerances. How will a small tolerance help -alleviate jitters? Below is the result with -tolerance=0.01.

-

-

Camera jitters occur again! This suggests that an excessively small -value cannot fully filter out actions that can lead to camera jitters. -Let's try our final experiment with tolerance=0.1.

-

-

Camera jitters disappear, but the camera motion seems a little stiff. -These experiments show that an appropriate value of -tolerance to ensure the smoothness and robustness of the -camera.

-

Solution 2: adding low-pass -filter

-

Our improved samping perfectly solves camera jitters under unstable -fps, but it looks very stiff when it reaches the boundary of max damping -distance. Can we make it more realistic so that the object won't just -look stolid? Yes of course, we can add low-pass filter, or moving -average to our improved damping to achieve more smooth results.

-

Recall the algorithm of the improved damping: if , we just set to . -Instead of hard setting to , we introduce , the smoothed version -of the original delta residual . If holds, we calculate as an average of - and :

-

-

which can be iterated through a recursive form:

-

-

Note that gets -updated if and only if holds, -i.e., when the camera lies in the unstable area. The -use of is similar to -low-pass filters in the sense that they all filter out high-frequency -signals.

-

Below is s sample code implementation in file -Predictor.cs:

-
CurrentResidual = initial * Mathf.Exp(-k * deltaTime);
ResidualDifference = CurrentResidual - PreviousRedisual;

float result;

float tolerance = 0.1f;
float alpha = Mathf.Log(bonus) * dampTime / kLogNegligibleResidual;
float ratio = deltaTime / alpha;

if (ratio <= 1.0f + tolerance && ratio >= 1.0f - tolerance)
{
float beta = 0.001f;
CachedDeltaResidual = (1 - beta) * CachedDeltaResidual + beta * ResidualDifference;
result = initial - (CachedDeltaResidual + PreviousRedisual);
}
else
{
result = initial - CurrentResidual;
}
-

Let's try it out! With damping time and , we can achieve the -following damping result with low-pass filter:

-

-

Now the camera looks much more smooth and, flexible. What about -trying a smaller damp time, say, ? Here is the result:

-

-

The result is ok but sometimes it's still jittering. It is because a -smaller leads to a larger and thus a larger chance to -cause jitters. To solve this issue, we can set a larger tolerance , or we can have a smaller -. We adopt a of and see how it performs.

-

-

The camera now becomes smooth again.

-

We can measure this sort of instability more quantitatively. Below is -a graph plotting -during five seconds of camera trace with damp time . The original curve (in blue) -oscillates over time due to an instability of fps. The improved damping -method eliminates all the oscillation and makes the curve absolutely -plain. Empowered by low-pass filter, the curve becomes smooth without -loss of stability.

-

-

Below is the graph with damp time . As can be seen, even with improved -damping, the camera still has a chance to vibrate, and the original -curve, oscillates much more intensely than with . Employing the low-pass filter -gives a much smoother and stable camera motion curve, as expected.

-

-

Speaking of this, why can't we just soften our -improved damping assignment to where is a function of - parameterized by .

-

Assume -and , we first calculate ; -then calculate ; -last, we have . -For , we obtain . is a parameter controlling how fast the -value of grows from - to . The larger is, the larger mass will be -concentrated on the -side.

-

Below is the result with -and :

-

-

Not bad! The soft version of improved damping really makes the camere -smoother and less stiff than the vanilla improved damping algorithm. The -follow plot also shows that with soft parameterization, the camera -trajectory is much more natural with neglectable amount of -oscillation.

-

-

We also compare it to different and . Beolow is the result with and .

-

-

A larger makes the camera more -stiff, but is still better than the original improved damping -algorithm.

-

Below is the result with -and .

-

-

is less effective as the -magnitude of attenuation it applies to is not enough to compensate -for the osciallation the unstable fps brings about.

-

Let's try another damp time. The result with and shows as follows.

-

-

When , the oscillation is -more severe, as we've already stated above. What about ?

-

-

Better, but still not sufficient to mitigate the oscillation. Let's -try .

-

-

Almost perfect. We can conclude that a smaller needs a larger to offset the intense jitters resulted -from unstable fps. Besides, you can combine the soft improved damping -method and low-pass filters to achieve a smoother transition.

-

Solution 3: continuous -residual

-

Okay, let's forget all aforementioned solutions and revisit our -residual update formula at the very beginning:

-

-

Reformulate thie equation to the following form:

-

-

where is the speed of the -camera's follow target, and is a more generalized form of the damping function . -Theoretically, it can represent any function of interest.

-

Now, regarding as a function -with respect to , we can seek to -obtain its derivative:

-

-

We use the equality -because when you plug -into , you will get , implying .

-

What about derivatives with higher orders? We can calculate the -second-order derivative as follows:

-

-

It is a nice form which bridges the first-order derivative and the second-order derivative -. In fact, for any --th order derivative, it can be -recursively calculated as:

-

-

Having all these derivatives, we can then expand using Taylor series and -calculate the difference to :

-

-

Note that if we are still choosing as out -damping function , the -derivative of it with respect to will be and the value at zero will be .

-

In practice, we first decide how many terms in the coefficient term -should be taken in, and then sum them up and multiply with the velocity -term, the result of which is denoted by . The residual at the current -frame, can be readily computed as . To save -computation, we can first cache -up to a threshold, say , and -then using the formula of geometric series to efficiently compute the -coefficient sum.

-

To estimate its error, we use the Lagrange remainder:

-

-

Decompose it:

-

-

where -and as we assumed. We can -see that the error is asymptotically negligible with respect to , especially when is small.

-

Recall that -where and - is the damp time. If is large, say 0.5 or even 1.0, the -value of will be -somewhat small so that a decent precision can be reached within few -steps of expansion, i.e., a small -say 2 or 3 could satisfy camera stability. However, if is small, say 0.2 or 0.1 or even -smaller, the value of -would grow larger, and then a larger might be needed to reach our expected -precision. This is in accordance with our observation that a smaller - generally leads to a more -unstable camera trajectory. We will show this soon.

-

Let's first try and . Recall that is the maximum order of derivatives we -use to approximate the residual difference. means that we only use in the coefficient term. Here is -the result:

-

-

Looks nice! What about setting ?

-

-

Not much difference, but a little bit smoother. Let's try respectively with and . First comes .

-

-

It's okay but it seems too fast when the cube comes back to -stillness. How about ?

-

-

Now everything gets worked! Next, let's set smaller, which generally won't be used -in actual gameplay but as a test it's worth a try. We set and try different to see how they influence our camera -trajectory.

-

Here is the result with :

-

-

Okay... a total mess. Try :

-

-

Unfortunately, the cube always stays behind the camera. Now :

-

-

Forget about it ... Let's try :

-

-

Things are getting better! At least it does not shake anymore and -begins to stay at the right position. I bet is better:

-

-

It's close! Last, we try :

-

-

Finally, the camera disposes everything well. As we can see from the -process, a small requires a large - to reach the minimum acceptable -precision. I hope you never have the chance to use such a small , and if it happens, cache enough orders -of derivatives or it would be prohibitively expensive to compute at -runtime.

-

To further understand why this method solves the jittering issue, we -take a deeper look at the expression of derived above. -This is an ODE and we solve it out (proof left to the readers):

-

-

Here I've expanded as . We cannot -directly use this explicit expression to calculate because there is no -correct time stamp when game is running. What we only have -is the previous frame's residual and the elapsed time at this frame -. And as the velocity may change over time, a closed-form of - cannor serve our purpose well. -We can only incrementally calculate camera residuals at each frame based -on what we currently have.

-

is a monotonic increasing -function, and of course, it's continuous. The continuity ensures that -the camera trajectory is always smooth and never jitters, if fps is -sufficiently high (over one thousand I suppose?).

-

For the original discrete residual, its velocity is:

-

-

where is from -Lagrange's Mean Value Theorem. Note that I add a tilde symbol over to distinguish it from the one from the -continuos version above.

-

This is another ODE. We can solve it out (proof left to the -readers):

-

-

Note that we solve the ODE with respect , the increment time rather than -the absolute time . So, we -introduce an initial value to control what the -initial value of residual is at this frame, is now the elapsed time for this frame -satisfying and .

-

The following graph shows that how the function changes with -different and . It can be -noticed that this function is very sensitive to the input , the elapsed time at this frame. A -small change of the input would significantly change the sign of , thus causing camera jitters. -We also notice that a smaller , -derived from a smaller , pushes -the function leftwards, which also makes it more vulnerable to -inputs.

-

-

Below is a comparison between five damping algorithms introduced in -this article, including the original damping. Damp time is set to 0.2. We observe significant -stability improvement when using any of the four proposed damping -algorithms. You should be careful when choosing the most appropriate -algorithm because the situation on which you intend to use damping. How -unstable is your fps? What is the damp time ? How is the tracked object moving? You -should experiment with these algorithms and choose the one that best -suits your needs.

-

-]]>
- - 游戏 - 相机 - - - 数学 - 随笔 - 计算机 - 相机 - Unity - Cinemachine - Damping - -
- - Motion Matching -- 概念与发展 - /2022/03/27/08/38/ - 这是我在组内分享的一次关于Motion -Matching基本知识的介绍,放在此备份。

- -

Motion -Matching - 概念与发展

-]]>
- - 游戏 - 动画 - - - 数学 - 随笔 - 计算机 - 游戏 - 动画 - 机器学习 - 深度学习 - -
- - Midjourney和NovelAI不完全使用指南 - /2022/10/22/17/37/ - 显然,AI绘画已经发展到了一个新的阶段。尽管现在市面上已经有不少AI绘画工具,但要熟练地掌握使用它们也绝非易事。本文从实践出发,为读者讲解如何更好地使用Midjourney和NovelAI这两个时下火热的AI绘画工具,但由于笔者使用时间较短,无法完全驾驭AI绘画工具,因此本文是“不完全指南”。如果本文有任意谬误或缺漏,希望读者能不吝指出。

- -

Midjourney不完全使用指南

-

太长不看版:使用规范

-
    -
  1. 首先根据公式 Prompt = 构图说明 + 画面内容 + 美术风格 + -光影设置 + 其他描述 + 参数列表 按照下面的模板写Prompt: -
      -
    • 人物特写:a {headshot | closeup} portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • -
    • 人物半身照:a {medium shot | over the shoulder shot | head and shoulder shot} portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • -
    • 人物全身照:a full body portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • -
    • 风景/建筑照:[content], [art style], [lighting], [other keywords], [MJ parameters] -上面用大括号{}括起来且用竖线|分割的内容是多选一,即从所有可选的关键词中选一个即可。用中括号[]括起来的是我们需要根据画面内容填写的。下面介绍中括号填写的基本规范:
    • -
    • [content]:是我们要填写的画面内容。如果是人物,主要填下面的内容(不一定都需要,也不一定完整,根据你的需求): -
        -
      • 什么人:{boy | girl | woman | old lady | warrior | witch | anthropomorphic white tiger | ...}
      • -
      • 脸部特征:{beautiful face | dedicate facial features | colorful tattoos on her face | ...}
      • -
      • 表情:{smile | crying | moaning | angry | ...}
      • -
      • 眼睛:{beautiful blue eyes | shining diamond eyes | ...}
      • -
      • 头发:{long curly black hair | flowing hair | braided hair | ... }
      • -
      • 装饰:{exquisite garland | opal decorations | wearing feather headdress | ...}
      • -
      • 穿着:{wearing tall black high heel boots | in purple and white kimono | ...}
      • -
      • 其他细节:{made of flower | rain on her face | ...} -如果是非人物,则一般不需要写这么细,只要描述大概是什么物体就好了。比如magnificent mountain, -interior of a cyber punk city等等。
      • -
    • -
    • [art style]:指定艺术风格,可以通过三种方式指定: -
        -
      • 直接指定主题/绘画风格:比如cyber punk, -steam punk, japanese anime, -oil painting, realistic painting, -ink painting, ukiyo-e, -bookstory illustration等等;
      • -
      • 直接指定相关风格的艺术家:比如吉卜力风格可以说art by {ghibili | koji hoshino | hayao miyazaki},皮克斯/迪士尼风格可以说art by {pixar | disney},等等;
      • -
      • 直接指定作品:比如breath of the wild, -dark souls, world of warcraft, -lord of the rings等等。 这里推荐使用第二种方式。
      • -
    • -
    • [lighting]:画面的光影效果,一般来说使用{cinematic lighting | volumetric lighting}比较通用。但对人物,你偶尔还需要用{back lighting | rembrandt lighting | spotlight};对非人物,你可能还要{morning lighting | flare}等等。
    • -
    • [other keywords]:其他关键词包括: -
        -
      • 材质:{cubic | bronze | wood | liquid | glass | prism | smoke | plume | milky way | ...}
      • -
      • 色彩:{dark pink | rainbow | vibrant | warm color | black and white | monochrome | ...}
      • -
      • 形状:{star | torus | polygonal | low poly | interior | stellation | stellation | ...}
      • -
      • 其他一般默认加上用来叠BUFF的词:{intricate details | 8K | enchanting | masterpiece | octane render | unreal engine 5 | well composed | award winning | high resolution | ...}
      • -
    • -
    • [MJ parameters]:Midjourney要填写的参数,一般使用下面的参数组合(但也要有意识地灵活运用): -
        -
      • 人物特写照:{--ar 1:1 | --ar 3:4} --q 1.5
      • -
      • 人物半身照:{--ar 3:4 | --ar 9:16} --q 1.5
      • -
      • 人物全身照:{--ar 2:3 | --ar 9:16} --q 1.5
      • -
      • 非人物照:根据你的画面内容选择宽高比,如果是横板,则用--ar 16:9 --q 1.5;如果是竖版,则用--ar 9:16 --q 1.5;如果是加长竖版,则用--ar 9:32 --q 1.5
      • -
    • -
  2. -
  3. 写好之后,多次出图,你也可以尝试几次参数--test --creative--testp,一般来说效果会更好些,但注意风景图不要用--testp
  4. -
  5. 从所有的图中找到让你比较满意的图,使用UpscaleVariantRemaster功能对它们增强,反复出图;也可以继续在Prompt中增删细节,直到满意为止。
  6. -
-

关键词参考工具
-更完整的关键词参考
-艺术家参考
-风格参考 -其他人的作品参考1 其他人的作品参考2

-

概述

-

Midjourney是当前最流行的AI绘画工具之一,它部署在Discord上,因此你需要注册一个Discord账号才能使用。

-

所有的AI绘画工具最重要的就是如何写Prompts,也就是文本描述。在开始之前,你需要知道写Prompts的几个基本准则:

-
    -
  • 详略得当:描述越详细,图片越有可能接近你想的画面,但是也有更大的概率生成的图片质量更低;描述越简略,图片越多样化,质量也可能更高。但注意不要加太多细节,否则会图片会很低。一般来说,我们只需要写”意象“,而不要写得过于具体。
  • -
  • 以短代长:少用超过10个词的句子,而用多个短语,每个短语描述画面的一个细节/部分/风格。即使要用长句,也不要太长,保持在20词以内。
  • -
  • 反复润色:不可能第一次生成的图片就完全符合你的想象,需要不断给Prompts润色修改,这不是一个简单的活,因此请保持耐心。
  • -
  • 具象描述:尽量用一些具象的名词、形容词,比如river, -rockstar, Zeus, landscape, -happy, -dark等等,不要用一些难以在现实中找到对应实体的词,比如knowledge, -notion, type等等。
  • -
  • 指定量词:显式指定对象的数量,如果是一个就用a,如果是多个就指定具体数量。
  • -
  • 描述风格:在多数情况下都需要增加风格关键词,比如cyberpunk, -surreal, abstract, -realistic,也可以指定一个或多个艺术家,比如hiroshi yoshida, -Max Ernst, MC Escher, -Yoji Shinkawa等。此外,你还可以指定具体的绘画形式,比如sketch, -woodblock print, oil painting, -watercolor painting等等。
  • -
  • 描述构图:可以显式指定构图,比如a portrait of, -an ultrawide shot of, a headshot of, -a closeup of等。
  • -
-

MJ官方文档:https://midjourney.gitbook.io/docs/

-

注册账号

-

Midjourney当前作为Discord的内置服务,你可以按照下面的步骤注册账号开始使用:

-
    -
  1. 登陆官网,点击Join the beta: -
  2. -
  3. 进入后输入昵称,加入Discord,如果你没有discord,可能需要根据提示注册一个,之后进入服务器: -
  4. -
  5. 进入一个以#newbies开头的频道,比如#newbies-117:
  6. -
  7. 在下方的输入框中输入/imagine,此时就能在弹出来的prompt框中输入你想要生成图片的文本描述了,比如我这里输入的是a -white flower is crying,稍等片刻,就能在聊天框中看到生成的4张图像了: -
  8. -
  9. 除了生成的4张图像外,下方还有两行按钮,分别是U1/U2/U3/U4和V1/V2/V3/V4,分别表示增大每张图的分辨率,以及为每张图重新随机生成。在点击增大分辨率之后,对应大图会重新发送在频道中,下方也会随之出现几个新按钮,见字如义: -
  10. -
  11. 如果你不想在公共频道,你也可以自己创建一个频道,然后邀请Midjourney -Bot到你的频道中。首先在左侧点击添加服务器;然后创建一个私有服务器;最后回到Midjourney的官方服务器,找到Bot,点击后把它添加至服务器即可。 -
  12. -
-

然后你就可以在你自己的服务器里愉快地玩耍了!

-

使用教学

-

首先记住下面的公式:

-

Prompt = 构图说明 + 画面内容 + 美术风格 + 光影设置 + -其他描述 + 参数列表

-

其中,“构图说明”也可以放在“美术风格”后面,但一般来说直接通过a portrait/closeup/wide angle shot of ...指定了。除了“画面内容”是必须的之外,其他的都可以省略。

-

建议初学者在这个网站这个网站找对应的关键词,多做尝试。

-

构图说明

-

构图说明指定是怎样的构图,比如特写、近景、远景等等。有下面基本的构图: -- 特写: closeup, portrait - -全身照:full body, full body portrait - -风景:wide angle, epic composition, -low angle, high angle

-

Prompt一般直接用a [composition] of ...开头,其中[composition]就是你选择的构图,比如你想要一个特写,你就可以说a closeup shot of ...或者a headshot portrait of ...;如果你想要一个全身照,你就可以说a full body portrait of ...

-

对于风景图,一般不用上述格式,而是直接以内容开头,把构图放在后面,比如vast grassland, wide angle, epic composition,首先说明内容是草原,然后再说用广角镜头和宏大构图。

-

下图分别是特写/中景/远景的例子,Prompt为a [composition] of an old asian lady --ar 3:4 --q 1.5,其中[composition]分别替换为closeup shot, -medium shotfull body portrait,同时把宽高比分别设置为3:4, -2:39:16。最后一张图是风景图,Prompt是vast grassland, wide angle, epic composition --q 1.5 --ar 32:9

-

-

你可以看到几种构图之间的差别,至于为什么要更改宽高比,详见下面的参数列表。

-

画面内容

-

画面内容指定画面内容。该部分根据需求可详可略,但一般都以多个短语组成,比如下面我想以凤凰为原型设计一个角色,全身照,有红色和黄色的花,穿着彩色华丽的装饰,因此输入的Prompt为a full body portrait of a phonix goddess, red and yellow blossoms, wearing rainbow opal accessories, exquisite decorations --ar 9:16

-

-

第一张图加了参数--test,因此细节更加丰富。

-

对于非人物也是相同的,比如我现在想设计一个亚特兰蒂斯城,它矗立在悬崖边,有着豪华的建筑,我就可以用the city of Atlantis on steep cliff, enormous beautiful palace, exquisit architecture --aspect 9:32 --q 1.5,得到下面的图:

-

-

前两张图用了--test

-

再次强调:描述内容的详略会极大影响生成的结果,越详细,生成的图片会越接近你想象中的画面,但有更大概率质量更低;越简略,越有可能生成非常酷的图片。因此,是否详略取决于你在脑海中是否已经有一个大致的画面,如果你完全没有想法,请尽量保持简略!

-

比如对我想要的凤凰角色,我不知道她具体是什么样子的,就只需要输入a full body portrait of a phonix goddess --ar 9:16就可以了,然后再不断添加细节(前两张图是原始Prompt,第三、四张图增加了red and yellow blossoms):

-

-

美术风格

-

美术风格指定图片的美术风格是怎样的。美术风格非常重要,它直接决定了图片内容是否与你想象中的相符。我们可以通过三种方法指定美术风格:(1)绘画风格,如realism, -realistic, abstraction, -impressionism, oil painting, -cover art, -comic book等等;(2)艺术家名字,如Rolf Armstrong, -Lois van Baarle, -Aubrey Beardsley等等;(3)与该风格有关的作品/游戏,如breath of the wild, -genshin impact

-
    -
  • 指定绘画风格:比如现在我想对上面的凤凰角色风格化,我可以指定不同的绘画风格,比如下图是依次指定为realism, -abstraction, watercolor painting, -oil paintingcartoon, anime的结果:
  • -
-

-
    -
  • 指定艺术家:相比指定风格,一个更好的方法是直接指定艺术家,比如我依次指定了下述艺术家Alphonse Mucha, -Alyssa Monks, Andreas Rocha, -Miyazaki HayaoEric Lacombe,所生成的图片是:
  • -
-

-

你也可以指定多个艺术家,但最好它们风格相似。你可以在这个表里找到一些参考艺术家。

-
    -
  • 指定相关作品:你还可以显式指定作品,下面的图依次显式指定了作品naruto, -breath of the wild, dark soul, -genshin impactminecraft
  • -
-

-

一般来说,推荐直接指定艺术家,辅之以绘画风格和相关作品,注意这三者之间的风格要尽量保持一致。当混用的时候,艺术家放在前面。

-

光影设置

-

图片的光影也是重要的一部分,我们可以直接指定光影的类型。比如我们以vast grassland with a lake in the center, a giant tree growing by the lake, --ar 16:9 --q 1.5为基础Prompt,分别考虑下述光影moody lighting, -morning lighting, cinematic lighting, -soft lighting, volumetric lighting, -rembrandt lighting, -godrayschiaroscuro

-

-

除了风景图之外,人物也可以应用不同的光影。下面以a full body portrait of a phoenix goddess, red and yellow blossoms, wearing rainbow opal accessories, exquisite decorations --ar 9:16 --q 1.5为基础Prompt,同样依次加入上面的光影设置:

-

-

可以看到,光影能够影响画面的整体风格,因此,根据内容选择一个合适的光影至关重要。

-

其他描述

-

除了上面的构成要素外,你也可以增加其他你想要的关键词,大致可分为下面几类。

-

材质

-

材质(Material/Texture)也可以用来描述画面的整体风格和细节,比如cubic就可能会使画面出现方块状物体。

-

下面以a beautiful moon above the desert, the moon is in intricate details, marvel cosmic, Cory Loftis, Conrad Roset, epic composition, low angle, dramatic lighting, spotlight, greyscale, cubic, [material], psychedelic, 8k --ar 2:3 --q 1.5为基础Prompt,分别使用材质cubic, -bronze, carbon fiber, foil, -glass, wood, -liquidsmoke, plume

-

-

可以看到,加入不同的材质会整体或局部地影响画面。carbon fiber使画面增加了颗粒感,glass让月亮出现了玻璃状物体,smoke, plume使得画面出现烟雾。当然这里由于Prompt前面的内容足够丰富了,导致部分材质的影响较小,所以区别不是很明显。

-

如果用简单的描述,再搭配材质关键词,效果会更明显些(Prompt为a tree, [material] --ar 9:16):

-

-

颜色

-

在Prompt增加一些与颜色有关的关键词有助于生成你想象中的画面。最简单的就是直接添加颜色词,比如red, -black, -blue等等,但这样效果不一定好。一般来说,我们可以增加带有色彩意向的词,比如rainbow, -vibrant, warm color, prismatic, -black and white, monochrome, -high contrast等等。

-

下面以a medium shot portrait of a beautiful women in dark green kimono, beautiful face, smile, blue eyes, long black hair, painted by Anne Stokes, rembrandt lighting, [color], ultra detailed, plume --ar 2:3 --s 5000为基础Prompt,分别以vibrant color, -prismatic, black and white, -monochrome, -colorful, rainbow为颜色关键词:

-

-

形状

-

你还可以添加形状关键词。这个形状不一定是常见的三角形、正方形,也可以是跟形状有关的物体,比如金字塔pyramid, -星星star,心形heart等等。

-

比如以a mountain, [shape] --ar 9:16为基础Prompt,考察下述形状star trapezohedron, -star prism, torus, polygonal, -polyhedron, interior, stellation, -square, heart, gear

-

-

polygon(多边形)是一种常见的风格,interior则会绘制物体的内部。

-

其他

-

一些其他对画面有帮助的词包括: - -细节程度:very detailed, spectacular details, -ultra detailed, intricate details - -清晰度:4k, 8k, high definition - -景深:depth of field, Canon 50mm - -情绪:enchanting, impressive - -气氛/环境:vintage, retro, -cosmic, celestial, seaside, -lucid dream, plume, Gossamer - -绘法:spatter, drips

-

你也可以增加其他的意象词。

-

参数列表

-

你可以在Prompt的最后添加一些参数,用于生成你想要的图片风格和质量。下面列出所有参数,其中加粗的是最常用的。

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
参数功能
/imagine呼出prompt,根据文本描述生成四张图片
/info查看当前正在运行的任务
/fast(/relax)切换为使用Fast/Relax GPU时间
/private切换为private模式,其他人不可见你的图片
/public切换为public模式,其他人可见你的图片
--hd使用旧算法,适用于抽象和风景图,图片分辨率更高
--ar 显式指定图片的宽高比,比如 --ar 16:9
--w 显式指定图片的宽度,比如 --w 320
--h 显式指定图片的高度,比如 --h 256
--seed 显式指定种子数
--no 生成的排除该关键词,比如--no -plants为去掉文本中的”plants”
--iw 设置prompt中的图片/文本权重比,默认0.25
--s 指定生成图片的风格化程度,值越大,图片越“抽象”,默认为2500
--q 指定图片质量,默认为1,值越大,细节越多,但耗时越长
--chaos 指定图片的随机性,值越大,生成图片越多样,范围[0,100]
--fast更快地生成图片,但质量会更低,近似于--q -0.5或--q 0.25
--stop 在n%的时候停止终止生成
--uplight在Upscale的时候用light版本,增加更少的细节,与原图更接近
--testp生成更接近现实的图片
--test生成更多样化、风格化的图片
-

指定宽高比:--ar

-

--ar指定了生成图片的宽高比,默认为1:1。宽高比会极大影响所生成的图片,比如下面的例子(基础Prompt为Utah teapot, wood --seed 1,从上到下、从左到右分别是宽高比为1:1,2:3,4:9,4:16,3:2,16:9,9:4,16:4): -

-

可以看到,对于同样的内容描述,宽高比直接影响所生成的内容,这是因为AI默认会“填满”整个图,所以在设定宽高比时,要注意你要生成的内容是怎样布局的。

-

指定风格化程度:--s

-

-s指定了图片的风格化程度,或者"天马行空度"。默认值为2500;20000会让你的图片看起来比较抽象,但也没有完全偏离你的prompt;但是60000会让图片完全无视prompt自由发挥。

-

下面举几个例子说明(基础Prompt为Utah teapot, wood --ar 16:9 --seed 1,左边是2500,中间是20000,右边是60000):

-

-

可以看到,--s 20000时图片的某些部分已经不符合输入的Prompt里,比如这里丢失了木头材质的信息;--s 60000时就开始放飞自我了。

-

对于这个参数,一般来说保持默认即可,如果你想要更多样化的结果,可以用5000~10000之间的值。

-

指定细节度:--q

-

-q指定了图片的质量,也就是细节的丰富度。默认值为1;2会让图片细节更加丰富,但生成速度也是原来的一半;5会让图片细节爆表,但也有可能导致图片整体效果很差。

-

下面举几个例子说明(基础Prompt为Utah teapot, wood --ar 16:9 --seed 1,左边是0.25,中间是1,右边是2):

-

-

可以看到,图片的细节度是递增的。当然这个例子过于简单了,导致细节度高的茶壶反而有点奇怪。

-

这个参数我比较喜欢用--q 1.5,谁不喜欢更多细节呢?

-

生成更逼真/风格化的图片:--testp, ---test

-

--testp让生成的图片更加逼真,而--test会让图片更加风格化。注意这二者都会只输入一张图片而不是通常的四张图。

-

下面有个例子(基础Prompt为Japanese house with pink roof --ar 16:9 --seed 1,左边为--testp,右边为--test):

-

-

左侧的房子很逼真,右侧则不完整。当然这个例子可能不够好,但足以说明这两个参数的区别。

-

值得注意的是,并不是增加了--testp生成的图片就一定是更现实的,但一般而言是更逼真的。比如你想生成一张二次元萌妹,加了--testp之后反而可能会让萌妹更加仿真,虽然我们都知道她不是现实中存在的。

-

加入参考图片

-

除了纯文字内容外,Prompt还支持插入图片,让生成的图片在内容和风格上参考给定的图片。

-

要插入图片,只需要把图片的链接放在Prompt开头就可以了,比如: -https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Utah_teapot_(solid).stl/1200px-Utah_teapot_(solid).stl.png Utah Teapot --ar 16:9 --seed 1 -这个Prompt最开始的链接就是图片地址,然后就是常规的文本内容,把原图和生成的图片做个对比:

-

-

再对比一下没有参考图生成的图片Utah Teapot --ar 16:9 --seed 1

-

-

显然,有参考图生成的图片在风格和形状上都更接近所提供的图,而没有参考图所生成的图片差异较大。

-

此外,你还可以通过参数--iw控制参考图的权重,默认是0.25。下面再分别给出权重为0.5和1时所生成的图:

-

-

可以看到,AI在很努力地模仿参考图的颜色、形状,但仍然颇有难度。一般来说,用默认的数值就可以了,如果你想要参考图的权重更大些,设置为0.5也足够了。

-

实战操作

-

我们把上面说的综合起来使用给几个例子。

-

吉卜力风格的风景

-

第一个例子,我想生成一只小船在水村中航行的图,村落有着丰富的细节,吉卜力的风格,同时增加一些晨光。我可以用这个Prompt:A boat ride through a flooded seaside village, beautiful elaborate architecture, painted by Miyazaki, Nausicaa Ghibli, morning lighting, high saturation, spectacular details, epic composition, wide angle, low angle --ar 9:32 --q 1.5,经过几轮比较随意的迭代,我找到下面几张还不错的图:

-

-

黑暗系风格的怪物

-

在第二个例子中,我想设计一个黑暗系风格的怪物。女神似乎在一般的作品中是一个正面的形象,那如果是一个腹黑女神呢?从这个出发,我试着尝试让AI画出一个黑暗系的腹黑女神,有白色裙子、邪恶笑容、黑色翅膀和金色花饰,绘画风格偏现实主义,艺术家选定为Dorothea -Tanning。

-

最后我把Prompt设定为a full body portrait of a wicked goddness, beautiful white dress, evil smile, red eyes, black wings, shining gold flowers on her hair, concept art, photo realistic, painted by Dorothea Tanning, back lighting, dramatic lighting, greyscale, intricate details, bold brushstrokes, mystical --ar 2:3,给出了下面的几张图(最后两张图使用了--testp):

-

-

上面的图比较明显的不足是人物脸部,尤其是眼睛都没有得到很好的处理,这是当前MJ画全身照的缺点。当我们画半身照人物特写的时候一般没有这个问题。

-

蒸汽朋克风格的建筑

-

第三个例子,我们想画一个蒸汽朋克风格的建筑,细节越多越好。坐落在水边,有丰富的光影,整体基调呈现暖色。

-

因此,我们选用Prompta beautiful magnificent steampuck building by the seaside, view from the sea, rigorous architecture, ultra realistic, epic composition, wide angle, close up, morning lighting, volumetric lighting, warm colors, intricate details, 8K, hd, unreal engine, enchanting --ar 9:32 --test --creative,生成了下面几幅图:

-

-

加入一点艺术家得到下面的图(图一二painted by Earl Norem, Edwin Lord Weeks,图三painted by Elizabeth Shippen Green,图四Ford Madox Brown,图五Farel Dalrymple,图六François Schuiten,图七Franz Marc,图八Georges Rouault):

-

-

在实验的过程中发现:不要将--testp用于风景图,否则会有奇怪的东西;相反,在人物图上用--testp效果很好。

-

使用建议

-

基于上面的使用方法和我自己的实验,初步建议大家在使用的时候遵循下述规范:

-
    -
  • 重视参数--ar!很有时候宽高比会严重影响生成的图片,即使输入的其他内容完全一致。比如你想画个人物肖像,如果你的宽度太小无法容纳一张脸,那么AI就完全不会生成正确的肖像画;而如果宽度太大,则可能会出现多个人或者其他不必要的元素。一般来说,宽高比和画面内容的关系如下: -
      -
    • 人物特写/Headshot:使用--ar 1:1或者--ar 3:4,并搭配headshot, -closeup, portrait等关键词
    • -
    • 人物半身照/全身照/角色设计:使用--ar 3:4--ar 9:16,搭配full body, -head and shoulder shot, over the shoulder, -medium shot等关键词
    • -
    • 风景图/远景:使用--ar 16:9,搭配landscape, -establishing shot, epic composition等关键词 -宽高比的选择完全取决于你想要生成怎样的内容,如果你想生成一个竖版的风景图,也完全可以使用--ar 9:16,总的原则就是!!#e06666 -画面内容与宽高比保持一致!!
    • -
  • -
  • 重视艺术风格!一个合适的艺术风格可以给你的画面带来极大的改变。当你需要偏现实的风格时,可以尝试realistic, -photo realistic, -ultra realistic等关键词,然后去找合适的现实主义风格的艺术家。当你需要特定的风格时,请精准描述艺术风格,比如浮世绘ukiyo-e,油画oil painting,流行艺术pop art,赛博朋克cyber punk,封面画cover art, -吉卜力Ghibli等等,这需要你对现有的艺术风格有比较丰富的了解!很多时候并不是你画不出来,而是你找不到对应的风格。你可以使用同一风格的多个艺术家作为关键词让画面更加倾向该风格。
  • -
  • 重视参数--test--testp!有时候仅用普通的2*2图片不能得到比较好的结果,尤其是Prompt较长的时候。此时,可以多用一下参数--test--testp,也许会带来意想不到的结果。注意,--testp不要用于风景图。
  • -
  • 重视参考图!尽管本教程没有过多阐述参考图的效果,但是当你手头有很多参考图时,不妨直接使用它们。记得调整参考图的权重--iw
  • -
  • 重视”魔法“关键词!有一些比较通用的关键词,比如intricate details, -unreal engine 5, enchanting, -ornate, after effect, -well composed, elaborate, -Sony Alpha等等,可能会提升画面的细节效果,不妨多试试它们。
  • -
  • 多尝试,出一张效果好的图需要运气,也需要认真地调试。
  • -
-

最后奉上几张AI绘制的浮世绘风格的图片,希望大家使用愉快 ;p

-

-

参考资料

-

MJ官方文档
-关键词参考工具
-更完整的关键词参考
-艺术家参考
-风格参考
-其他人的作品参考1
-其他人的作品参考2

-

NovelAI不完全使用指南

-

太长不看版:使用规范

-
    -
  1. 首先按照公式 Prompt = 起手叠BUFF + 构图说明 + -画面内容 + 画面风格 + 光影设置 + 颜色设置 + 其他意象 -去写Prompt,具体来说:

    -
      -
    • 起手叠BUFF:把下面的内容放到你要写的Prompt最前面,起手BUFF还是比较重要的: -{masterpiece}, {best quality}, {ultra-detailed}, illustration, beautiful, 8K, small breasts -最后的small breasts可以换成madium breasts或者其他(你懂的)。
    • -
    • 构图说明:人物在画面中的位置、大小、角度等等,常用的有portrait(特写)、medium shot(半身照)、full bodyupper body(全身照)、dutch angle(倾斜镜头)、wide angle(广角镜头)、side view(从侧面看)、back view(从后面看)等等。
    • -
    • 画面内容:画面里需要包含的各种内容,可以从人物本身和背景两个角度分别描述。人物内容包括头发、脸部、眼睛、肩膀、耳朵、配饰、手、服装、手套、鞋子等等,但首先需要指定包含几个人,比如1 girl, -solo;背景就根据自己的需求增加内容即可,比如dragon background, -forest background, beautiful milkyway, -burning bettlefield等等。此外,你还可以使用关键词reference sheet生成三视图、设计图。
    • -
    • 画面风格:画面的美术风格,常用的包括realistic, -outline, sketch, flat color, -watercolor (medium), grey scale, -ukiyo-e, cover art, poster, -comic, art nouveau, cyberpunk, -sci-fi, wildstyle, 等等。
    • -
    • 光影设置:和MJ一样,设置画面的光影,主要包括:背光backlight, -电影打光cinematic lighting, 圣光holy light, -日光sunlight, 月光moonlight, -波光粼粼glistening light of waves, -金色光golden light等等,你也可以根据需求创造属于你的光影。
    • -
    • 颜色设置:使用颜色关键词让画面整体更偏向某种颜色。
    • -
    • 其他意象:你可以加入任意多的其他意象词为画面添加细节和内容,比如:阳光sunlight, -河流river, 水晶crystal, -棱镜prism, 冰ice, 浮动floating, -照射shine, 影子shadow, -装饰ornament/decoration/frills, 火焰flames, -火花sparks, 光晕flares, -核爆nuclear explosion, -飞溅的血splashing blood, -飞舞的花瓣flying petals, 等等。
    • -
  2. -
  3. 把下面的内容写到Undesired -Content中,然后再加入你想屏蔽的其他关键词: -{{{ugly}}},{{{duplicate}}},{{morbid}},{{mutilated}},{{{tranny}}},{breast},mutated hands,{{{poorly drawn hands}}},{{bad anatomy}},{{{bad proportions}}},extra limbs,cloned face,{{{disfigured}}},{{{more than 2 nipples}}},{{{{missing arms}}}},{{{extra legs}}},{{{{{fused fingers}}}}},{{{{{too many fingers}}}}},{{{unclear eyes}}},{{{fused hands}}},{{{fused leg}}},{{{bad feet}}},nsfw,lowers,bad anatomy,bad hands,text,error,missing fingers,extra digit,fewer digits,cropped,worst quality,low quality,normal quality,jpeg artifacts,signature,watermark,username,blurry,bad

  4. -
  5. Steps默认28,Scale默认为7,当然你可以根据实际需求调整这两个值。Samping使用默认的k_euler_ancestral即可;

  6. -
  7. 对你比较满意的图使用VariationEnhance,反复迭代,直到满意为止。

  8. -
-

概述

-

NovelAI是基于Stable -Diffusion模型改进的AI绘画工具,它擅长绘制二次元人物图,虽然也可以把它当作综合性的绘画工具,但是生成的图片偏写实,质量不如Midjourney。

-

写NovelAI -Prompt的基本准则是:用关键词(或者称为Tag)描述,而不要用短语甚至句子。关键词包括画面内容(人物头发、眼睛、表情、服饰、姿势、手部、胸部、肩部,等等)、画面风格、构图设置、光影设置、颜色设置、意象词和叠BUFF词等等。

-

总的来说,写NovelAI -Prompt相比MJ更容易些,但要实现精准调教仍然难度很大。下面会详细介绍。

-

NovelAI官方文档
-关键词参考

-

注册账号(官方)

-
    -
  1. 登陆官网,注册并登陆账号。

  2. -
  3. 之后在打开的页面上点击“Generate Images”,或者直接通过网页进入:

  4. -
  5. 最后输入Prompt并调整右侧参数开始使用:

  6. -
-

本地版本

-

To do

-

使用教学

-

NovelAI的Prompt跟MJ差不多,主要遵循下述公式: Prompt = -起手叠BUFF + 构图说明 + 画面内容 + 画面风格 + 光影设置 + 颜色设置 + -其他意象

-

在介绍每个部分之前,需要先讲解下NovelAI各个参数的作用。

-

NovelAI的参数

-

-

从上到下,从左到右:

-
    -
  • Prompt:在这个地方输入你的Prompt,使用大括号{}增加一个关键词的权重,使用中括号[]去减少关键词的权重,支持嵌套,比如{{magical}}就表示生成图像的时候会特别关注magical的内容,而[[[green]]]则表示生成时尽量避免生成绿色的内容;
  • -
  • 分辨率:在这里设置你图像的分辨率,可以使用预设,也可以手动输入,这个参数非常重要,同MJ,要和你生成的内容相匹配
  • -
  • Number of images:生成图像的数量;
  • -
  • Undesired -Content:输入不想要AI生成的内容的关键词;
  • -
  • Add Quality Tags:默认勾上就行;
  • -
  • Steps:生成一张图需要的步数,步数越大,生成的时间越长,而且效果也不一定好,一般使用默认值28就好了,除非你已经找到一个很好的Prompt想要增加更多的细节;
  • -
  • Scale:控制所生成图像匹配你输入Prompt的程度,值越小,画面越风格化和柔和,值越大,画面越细节和尖锐,但设置过大可能导致效果变差,一般来说使用小于10的值;
  • -
  • Sampling:生成时的采样方法,一般而言使用默认的即可。
  • -
-

-

在生成图像后,会多出来一排选项,其中比较重要的是后面两个:

-
    -
  • Variations:生成当前图片的变体,在细节上会有不同,但大体都是一样的;
  • -
  • Enhance:对当前图像进行增强,会较显著地增加细节。但注意不要把Noise调太高。
  • -
-

起手叠BUFF

-

NovelAI要把关键信息放在Prompt的前面,因此我们一开始就要叠BUFF,可以先无脑加入下面的BUFF,然后再根据你的需求自行添加: -{masterpiece}, {best quality}, {ultra-detailed}, illustration, beautiful, 8K, small breasts

-

注意上面的最后一个BUFFsmall breasts限制了生成角色胸的大小,对于女性角色必须要有(否则全是涩图)!你如果不喜欢平胸,可以用medium breasts,或者你生成的不是女性,就把这个去掉即可。

-

构图说明

-

和MJ一样,可以用portrait表示特写,用medium shot/upper body表示上半身构图,用full body shot表示全身照。

-

除此之外,还可以用dutch angle表示倾斜镜头,用wide angle表示广角镜头,用low angle表示低角镜头,用depth of field增加景深,用side view表示从侧面看,等等。你可以根据自己想象中的内容选择合适的组合。

-

!!#3d85c6 这个网站有一些主要的关键词:https://aitag.top/ -。下面的所有内容都可以去参考这个网站,不再赘述。!!

-

比如下面我用了一些不同的构图关键词去生成a beautiful girl(关键词分别是portrait, -medium shot, full body shot, -full body shot, dutch angle, -portrait, dutch angle, depth of field, -portrait, side view, -full body shot, back view, -full body shot, from above):

-

-

画面内容

-

你首先需要明确图片中包含几个角色,一般来说是一个,那么你只需要加入solo1 girl/1 boy即可。如果是两个,就是two girls,以此类推。

-

然后,你需要描述这个角色的各种细节,可以从下面角度考虑(不一定都要,看你需求):

-
    -
  • 头发:disheveled hair, floating hair, -azure hair, long hair, -short hair, beautiful hair, -white hair, curly hair, bob hair, -polytails, updo, -twintailsside blunt bangs,等等
  • -
  • 脸部:tears, cold attitude, -smile, sad, annoyed, -delicate beautiful face, -detailed face,等等
  • -
  • 眼睛:Lavender eyes, crystal eyes, -bright eyes, beautiful detailed eyes, -half closed eyes, hollow eyes, -blank stare, rainbow eyes, -gradient eyes, sparking eyes,等等
  • -
  • 肩膀:bare shoulder, off shoulder
  • -
  • 耳朵:pointy ears
  • -
  • 手:outstretched arms, arms behind back, -hands on hips, hand on own face, -hugging own legs, hand in own hair, -holding flowers,等等
  • -
  • 配饰:gold accessories, white lightsaber, -tail, scarf, armor headdress, -ribbon, neck ribbon, hair ribbon, -halo, necklace, wings, -tassel, earrings, wizard hat, -headphone, -red swordfloral print, 等等
  • -
  • 服装:detailed mechanical armor, -detailed organdie dress, skyblue dress, -princess dress with delicate gold metal decorations, -witch dress, white thin detailed cloak, -summer long skirt, angel suit, -very long dress, -translucent fluttering dress with lacekimonotrench coat, -cheongsampettiskirt, -lolita gothicpleated skirt, 等等
  • -
  • 手套/袖子:detailed white gloves, -elbow gloves, sleeveless, -wide sleeves, large top sleeves, 等等
  • -
  • 鞋子:barefoot, thigh boots, -getauwabaki, 等等
  • -
-

建议平时可以多看别人的关键词然后记录下来。

-

使用不同的组合并加入不同的权重可以产生你想要的效果,比如下面的例子:

-

-

除了角色本身的细节之外,你还可以指定背景,比如没有背景no background, -以龙为背景dragon background/loong background,以森林为背景forest background,大火为背景fire background/burning background,如下(不同的背景需要不同的权重):

-

-

特别说明:如果你想要生成人物设计图(即三视图),你可以用reference sheet,并同时修改分辨率

-

-

画面风格

-

顾名思义,就是需要选择图片的美术风格,下面有一些供参考的风格及其关键词选择:

-
    -
  • 写实:realistic, photorealistic
  • -
  • 素描:sketch, rough sketch, -pencil sketch
  • -
  • 描边:outline
  • -
  • 线稿:lineart
  • -
  • 像素:pixel art
  • -
  • 平涂:flat color
  • -
  • 平面着色:flat shading(注意和平涂不一样,下有例子)
  • -
  • 水彩:watercolor (medium), -watercolor pencil (medium)
  • -
  • 单色:monochrome, spot color, -greyscale
  • -
  • 浮世绘:ukiyo-e
  • -
  • 苏维埃海报:soviet poster
  • -
  • 封面:cover art
  • -
  • 漫画书:comic book
  • -
  • 动漫:comic
  • -
  • Q版:chibi
  • -
  • 复古艺术:retro artstyle
  • -
  • 新艺术派:art nouveau
  • -
  • 年代:1970s, 1980s, -1990s
  • -
  • 赛博朋克:cyberpunk
  • -
  • 狂野风:wildstyle
  • -
  • 科幻:sci-fi
  • -
  • 奇幻:fantasy
  • -
  • 传统日本风格:traditional Japanese art
  • -
-

建议把风格使用至少三个大括号{{{}}}甚至更多包裹起来进行强调,确保可以生成正确的风格图。

-

你可以选择同一个风格里的多个关键词,或者结合不同的风格。比如你可以融合像素风pixel art和奇幻风fantasy形成像素奇幻风。但最好不要融合超过两种风格,否则生成结果未知。

-

下面是一些例子(依次是{{{flat color}}}, -{{{flat shading}}}, -{{{soviet poster}}}, {{{flat color}}}, -{{{outline}}}, {{{sketch}}}, -{{{{{traditional media}}}}}, -{{{ukiyo-e}}}, {{{outline}}}, -art nouveau, -{{{pixel art}}}, {{fantasy}}, -{{{black and white}}}, {outline}, {flat shading}, {flat color}, {concept art}, {lines}, -{{{{wildstyle}}}}, {flat color}, -{{{{{{{{wildstyle}}}}}}}}, {cyberpunk}, {{{outline}}}, -{traditional japanese art}, {anime}, {fantasy}):

-

除了显式指定美术风格之外,你还可以指定艺术家和作品让画面偏向某种特定的风格。但是和MJ不同的是,NovelAI并不像MJ那样非常依赖艺术家,一般不加,或者最多只加一个艺术家或作品即可。比如下面我分别指定了ghibili, -Hayao Miyazaki, -breath of the wilddark soul

-

-

可以看到,加入艺术家和作品并没有想象中的那样有效,所以推荐不加。

-

注:当然如果你非常熟悉某个艺术家,那加入艺术家也是可以的,但一般来说需要给艺术家比较强的权重模型才会生成比较相似的风格,而且也不是所有艺术家都支持的,还是建议多做尝试。这个表是已记录的一些艺术家,可以根据风格先在谷歌上搜索,然后自行尝试。

-

光影设置

-

这里的光影设置和MJ是一样的,比如下面的光影: -背光backlight, 电影打光cinematic lighting, -柔和光soft lighting, -体积光volumetric lighting, -点光(聚光灯)spotlight, 圣光holy light, -日光sunlight, 月光moonlight, -波光粼粼glistening light of waves, -金色光golden light

-

-

颜色设置

-

颜色没太多好说的,如果你想要画面整体偏某种颜色,直接加入颜色关键词即可。 -但更好的方法是直接指定某个物体的颜色,比如red eyes, -cyan hair,以实现精准控制。有时候需要加大物体的权重,避免这个颜色控制了其他部件。

-

其他意象

-

其他意象词一般用来增加前景和背景的丰富度,以及人物身上的细节,比如下面的一些关键词: -羽毛feather, 自然nature, -叶子leaves, 阳光sunlight, -河流river, 水晶crystal, -棱镜prism, 冰ice, 齿轮gear, -流动flowing, 浮动floating, -照射shine, 影子shadow, 时钟clock, -装饰ornament/decoration/frills, 火焰flames, -火花sparks, 光晕flares, -核爆nuclear explosion, 闪电lightning, -飞溅的血splashing blood, -飞舞的花瓣flying petals, 微风breeze, -风wind, 雨rain, 云clouds, -烟smoke, 雾mist, 纱yarn, -沙sand, 星尘stardust, -银河milkyway, 旋转swirling, -头骨skull, 骨骼skeleton, -几何geometric, 立方体cubic, -多边形polygon

-

总之你可以添加任何你想要在图片中出现的意象词,但要注意和整体画面内容的搭配。你可以通过调整词的权重控制意象出现的频率。

-

还是建议找一些相关的参考图和别人给的关键词,多做尝试。

-

负面关键词

-

负面关键词是你不想让它出现在图片中的内容,填入Undesired -Content中即可。虽然是根据你不想要的内容去选择负面关键词,但是也要一些通用的负面关键词。下面是默认添加的关键词,其他关键词根据需求自行添加: -{{{ugly}}},{{{duplicate}}},{{morbid}},{{mutilated}},{{{tranny}}},{breast},mutated hands,{{{poorly drawn hands}}},{{bad anatomy}},{{{bad proportions}}},extra limbs,cloned face,{{{disfigured}}},{{{more than 2 nipples}}},{{{{missing arms}}}},{{{extra legs}}},{{{{{fused fingers}}}}},{{{{{too many fingers}}}}},{{{unclear eyes}}},{{{fused hands}}},{{{fused leg}}},{{{bad feet}}},nsfw,lowers,bad anatomy,bad hands,text,error,missing fingers,extra digit,fewer digits,cropped,worst quality,low quality,normal quality,jpeg artifacts,signature,watermark,username,blurry,bad

-

实战操作

-

下面还是给几个实战操作的例子。

-

和服风的设计稿

-

第一个例子,我们想要一个三视图设计稿,主角是一个穿着和服的传统日本女孩,盘发、头上有红色的花、化妆、精致的手镯、漂亮的印花。因为是设计图,所以要调整下分辨率,用默认的Landscape就好了。因此,我用的Prompt是:{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, small breasts, full body, solo, a japanese girl, {{{{{reference sheet}}}}}, flat color, concept art, brown hair, red flower in hair, {updo}, smile, beautiful face, beautiful makeup, delicate bracelet, {beautiful kimono with intricate floral print}

-

下面是生成的一些图(尝试了不同的Steps和Scale,Steps大则细节更丰富,Scale越小则多样性越强,但建议Steps<=40, -Scale>=6。最后一张图给和服加了blue):

-

可以看到,人物的手和脚是重灾区,但其他地方还是可以的。另外,活用Enhance能够极大地修复手的问题,关键在于Strength的参数不要太大(0.3左右),Noise设置为0或者非常小。

-

机甲少女全身照

-

第二个例子,我想要画一个机甲少女的全身照,有着冷酷的表情、红色的眼睛、脸上有纹身、拿着一把红色的刀,我并不太想指定其他过多的元素,但是想要图片的背景是弥漫着硝烟的战场,空中也飞舞着火星。因此,我使用的Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, small breasts, full body, depth of field, solo, a mechanical girl, detailed mechanical armor, detailed mechanical body} {holding a red sword}, disheveled hair, short hair, cold stares, half-closed eyes, {{dark red eyes}}, gradient eyes, ruined battlefield background, {detailed background}, burning buildings, splashing sparks, flames, holy light

-

下面是生成的图(使用了不同的Steps和Scale,最后四幅图修改了机甲的颜色): -

-

狂野赛博艺术图

-

作为我们的第三个例子,我们将探索wildstyle这个风格与其他关键词的组合会得到怎样的效果。wildstyle意味着丰富的色彩,尤其是大面积深色的应用。我们将融入赛博朋克的元素,并搭配不同的关键词看AI会给出我们怎样的结果。我使用的基础Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, {{{{wildstyle}}}}, beautiful, small breasts, solo, {{a girl}}, cyberpunk

-

下面是生成的图(每张图都调整了wildstyle的权重,附加Prompt依次是, -{flat color}, -{{flat color}}, {colorful}, -{{flat color}}, {{outline}}, -sci-fi, -{flat color}, beautiful kimono with cherry floral print, -{flat color}, watercolor (medium), -{{{pixel art}}}):

-

结果发现,wildstyleflat color, -outline等关键词搭配效果很好。

-

华丽高贵女神范

-

第四个例子,我想要得到一个华丽高贵的女神,被白色的花簇拥着,穿着华丽的白色礼服,点缀金色丝边,戴着项链、手镯、花环,蓝色的发光的眼睛。分别尝试不同的构图,即特写、半身、全身,和不同的风格,对背景不做要求。采用的Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, very detailed, small breasts, [composition], [style], solo, a royal goddess, disheveled hair, detailed blue eyes, gradient eyes, glowing eyes, vibrant colorful garland, gorgeous princess dress with delicate gold metal decorations, {{white flower decorations}}, {{surrounded by flowers}}, exquisite bracelet, exquisite necklace, bare foot, {{flowing flowers}}, {{liquid}}。其中,[composition]填写构图,[style]填写风格。

-

下面是生成的一些图片(前两张是portrait, -第三四张是head and shoulder shot, -最后四张是full body;风格第一张是cartoon, anime, -第二张realistic, 第三张cartoon, anime, -第四张fantasy, flat shading, -第五张flat color, geometric, cubic, -第六张cartoon, anime, -第七张flat color, flowing,第八张dark magic, Cthulhu): -

-

使用建议

-
    -
  • 重视分辨率!道理和MJ一样,即图片的分辨率要和想要生成的内容匹配,不再赘述,详细请看MJ页面。
  • -
  • 尝试权重!NovelAI对关键词加权是非常重要的一个操作,有时候你写的关键词没有生效极有可能就是关键词的权重不够导致的,这时候多尝试嵌套几层大括号{{{}}}。一般来说,对
  • -
  • 叠通用BUFF!上面给出了最基础的通用BUFF,但是对某些类型的图来说,还有一些额外的BUFF可以叠,建议多看看对不同的美术风格、画面内容,别人怎么叠BUFF的,总结一套属于自己的BUFF表。之后有时间我也会帮大家总结。
  • -
  • 加入风格!加入风格(包括与风格有关的关键词)会有助于生成你想要的内容,但注意与MJ不同,NovelAI对艺术家的支持并不好,所以尽量不要用艺术家。加入flat shadingflat color偶尔会有奇效,其他的一些意向词比如flowing, -geometric也能创造很好的风格。
  • -
  • 多尝试参数!NovelAI的参数虽然没有MJ多,但是调试更加困难。一般来说用Steps=28, -Scale=7的默认参数能够得到还不错的效果,如果你发现怎么改关键词都不生效的话,果断尝试修改参数吧(当然也有可能是NovelAI的训练集中没有你输入的内容,弃疗吧)!
  • -
-

参考资料

-

NovelAI官方文档
-NovelAI图像生成注意事项
-关键词参考
-元素法典——Novel AI -元素魔法全收录(第一卷)

-]]>
- - 随笔 - - - 随笔 - 计算机 - 游戏 - 深度学习 - 工具 - 绘画 - -
- - A Brief Note on Version Control and Project Organization - /2022/11/21/16/03/ - This is a brief note about the e-book Version -Control and Project Organization Best Practice Guide present at -Unite 2022. In this book, we can learn fundamental concepts of version -control and some best practices for organizing a Unity project. If you -are new to Unity, or you prepare to set up a larger scale Unity project, -this may be what you need.

- -

Foundational concepts

-
    -
  • Version control enables you to keep a historical -track of your entire project. It facilitates your collaboration with -your team through trackable and revertible commits organized in the form -of timeline.
  • -
  • Why use version control -
      -
    • Useful for making experimental changes
    • -
    • Easy iteration
    • -
    • Avoid conflict
    • -
  • -
  • Centralized vs. distributed version control -
      -
    • Centralized: repository is resided in a dedicated server, and -changes are fetched from and sent to the repository directly. To avoid -conflicts, users can lock files for modification, which is known as -checking out the file.
    • -
    • Distributed: users have local copy of the project and submit changes -whenever they want without always working on the latest files like on a -centralized system. But it costs a lot of space to store the entire -history changes.
    • -
  • -
  • Typical workflow -
      -
    • Centralized -
        -
      1. Update your working copy with changes from the server
      2. -
      3. Make your changes
      4. -
      5. Commit your changes to the central server
      6. -
    • -
    • Distributed -
        -
      1. Pull any remote changes into your local repo
      2. -
      3. Make changes
      4. -
      5. Commit changes
      6. -
      7. Push changes back to the remote repo
      8. -
    • -
  • -
-

Best practices -for organizing a Unity project

-
    -
  • Folder structure -
      -
    • Recommendations: -
        -
      • Document your naming conventions and folder structure.
      • -
      • Be consistent with your naming convention.
      • -
      • Don't use spaces in file and folder names.
      • -
      • Separate testing or sandbox areas.
      • -
      • Avoid extra folders at the root level.
      • -
      • Keep your internal assets from third-party ones.
      • -
    • -
  • -
  • The .meta file: it holds information about the file -which it is associated with, e.g., Textures, meshes, audio clips that -have particular import settings.
  • -
  • Naming standards: -
      -
    • Use descriptive names, not abbreviate.
    • -
    • Use Camel case/Pascal case.
    • -
    • Use underscore sparingly.
    • -
    • Use number suffixes to denote a sequence.
    • -
    • Follow document naming.
    • -
  • -
  • Workflow optimization: -
      -
    • Split up your assets: break levels into smaller scenes, using -SceneManager.LoadSceneAsync; break work up into Prefabs -where possible.
    • -
    • Use Preset to save asset settings.
    • -
  • -
  • Code standards -
      -
    • Decide a code standard and stick with it.
    • -
    • When using namespace, break your folder structure up by the -namespace for better organization.
    • -
    • Using a standard header.
    • -
    • Using script templates by creating an -Assets/ScriptTemplates folder.
    • -
    • You can also use your own keywords and replace them with an Editor -script implementing the OnWillCreateAsset method. -
      // /*-------------------------------------------
      // ---------------------------------------------
      // Creation Date: #DATETIME#
      // Author: #DEVELOPER#
      // Description: #PROJECTNAME#
      // ---------------------------------------------
      // -------------------------------------------*/
      using UnityEngine;
      using UnityEditor;
      public class KeywordReplace : UnityEditor .AssetModificationProcessor {

      public static void OnWillCreateAsset (string path)
      {
      path = path.Replace(".meta", "");
      int index = path.LastIndexOf(".");
      if (index < 0)
      return;

      string file = path.Substring(index);
      if (file != ".cs" && file != ".js" && file != ".boo")
      return;

      index = Application.dataPath.LastIndexOf("Assets");
      path = Application.dataPath.Substring(0, index) + path;
      if (!System.IO.File.Exists(path))
      return;

      string fileContent = System.IO.File.ReadAllText(path);

      fileContent = fileContent.Replace("#CREATIONDATE#", System.DateTime.Today.ToString("dd/MM/yy") + "");
      fileContent = fileContent.Replace("#PROJECTNAME#", PlayerSettings.product-Name);
      fileContent = fileContent.Replace("#DEVELOPER#", System.Environment.User-Name);

      System.IO.File.WriteAllText(path, fileContent);
      AssetDatabase.Refresh();
      }
      }
    • -
  • -
-

Version control systems

-
    -
  • Git: Fork, GitKraken, VS Code, VS, SourceTree, Sublime Merge.
  • -
  • Perforce (Helix Core): see here -to learn how to integrate Perforce into Unity.
  • -
  • Apache Subversion (SVN)
  • -
  • Plastic SCM: see here to learn more -about Plastic SCM.
  • -
-

-

Settings up -Unity to work with version control

-
    -
  • Editor project settings -
      -
    • Perforce: Edit -> Project Settings -> Version Control -> -Mode.
    • -
    • Plastic SCM: click the Plastic SCM icon in the toolbar on the top -right in Unity Editor.
    • -
  • -
  • What to ignore: Do not commit the Library folder, as -well as the .exe or .apk files.
  • -
  • Work with large files: teams prefer a centralized workflow where -large binary files would only on a central server with individual users -only accessing the latest version on their machines, rather than a -distributed one where many copies of historical files are stored on -local machines. If using Git, be sure to include Git LFS.
  • -
-

Best practices for version -control

-

Some suggestions you may need to make teamwork more efficient:

-
    -
  • Commit little, commit often.
  • -
  • Keep commit messages clean.
  • -
  • Avoid indiscriminate commits. It is important to understand that you -should only commit what you have changed in the project.
  • -
  • Get the latest -
      -
    • Git: pull -> edit -> pull -> commit -> pull -> -push.
    • -
    • Perforce: get latest -> check out files -> edit -> -submit
    • -
  • -
  • Know your toolset -
      -
    • Git: UI client
    • -
    • Plastic SCM: Gluon
    • -
    • Perforce Helix Core: built-in Unity Editor tools
    • -
  • -
  • Feature branches and Git Flow: main, hotfix, release, develop, etc. -Both Plastic SCM and Perforce have automated tools to help manage -merging branches back into mainline. Plastic SCM does this with the help -of MergeBot, -and Perforce uses Helix Swarm for -managing code reviews that can also be set up with automated -testing.
  • -
-

The biggest takeaway is the importance of clear team communication. -As a team, you need to agree on your guidelines

-]]>
- - 随笔 - - - 随笔 - 项目管理 - -
- - 《Assassain's Creed Odyssey》玩后感 - /2020/09/08/18/39/ - 115小时,主线+两个DLC通关。奥德赛是一个优秀的游戏,但不是神作,在走向RPG的道路上是系列作品的里程碑。人物塑造方面,奥德赛无疑是成功的,由于我选择的是温情 -路线,所以我看到的马拉卡的内心是无比渴望家庭的温暖的,正如奥德赛音乐集的“Odyssey(Modern -Version)”中的歌词"Travel in path alone, back to the warmth of -home"一般,踏在异乡的每一步无一不通往家。本作两个主要配角Phoibe和Brasidas,都令人印象深刻。在画面上,优良的美工沿袭了育碧式BUG,有可能在欣赏风景的时候会卡到墙里面,有时候还挺扫兴的。值得一提的是,奥德赛对希腊风情的刻画非常优秀,每一个同步点都可以疯狂截图作壁纸。本作比较难受的地方是剧情,三条主线除了家庭线比较完整之外,另外两条主线都虎头蛇尾,过多的无用支线严重稀释了主线的紧凑感,让人在玩到一半之后想去玩巫师三。DLC1的整体剧情我个人比较喜欢,但是一些细节处理尤其不妥,动机不足导致DLC1评分降低。DLC2流程很长,总体来说冥界剧情好于亚特兰蒂斯剧情好于极乐世界,不过对神之领域的描绘总体来说很精彩。如果说我对11.10发售的英灵殿有什么值得期待的话,可以用几句话总结:剧情不要拉跨,任务尽量优化;BUG可以少些,卡墙不动尴尬;风景依旧如画,壮汉赶紧来吧!

- -

剧情:整体精彩,细节拉跨

-

自奥德赛发行以来,“剧情”一直是被玩家广为诟病的一点,但是在做完所有任务(包括两个DLC)、体验完所有剧情之后,我倒是觉得奥德赛的剧情整体上非常优秀,但是和大多数玩家一样,我也想吐槽其中非常多的细节,我思考了一下,这大概是出于一种“恨铁不成钢”的心理。明明奥德赛的剧情可以做得非常棒,可以几无瑕疵,可以触及巫师三,但是阿育就是把这么一个差一点点就堪比完美的剧本甩到玩家脸上,香还是香的,但就是不情愿,被迫品尝一桌盛宴中编剧埋下的一坨坨屎。

-

世界观

-

奥德赛的世界观是建立在希腊伯罗奔尼撒战争期间,但显然,奥德赛不完全是一部纪实游戏,战争只是游戏的大背景,也是游戏开始的一个引子而已,到了后面,游戏的进展其实和战争已经没什么太大关系了。在这个大背景下,著名的时代人物,如伯里克利、苏格拉底、布拉西达斯都会悉数出场并成为推进主线过程中重要的人物,而另一大群体,“神教”,正贯穿了整个主线的发展。主角的冒险由神教展开,也从神教结束,神教作为核心暗线而存在,我们把它称为神教线。而剧情的实际主线则是马拉卡找家人的旅程,在途中,主角会踏遍希腊,经历诸多精彩的故事,最终找到家人,在若干年后再次相聚,我们把它称为家庭线。除此之外,奥德赛中还引入了很多神话元素,比如传奇动物、传奇生物、神之领域(主要在DLC2),它们以“神器”联系起来,游戏进行到中后期会出现一条新的主线,也就是让主角去搜集神器,我们称之为神器线

-
-奥德赛的地图很大 - -
-

所以,总的来说,奥德赛的世界观就是在希腊伯罗奔尼撒战争的时代大背景下,以家庭线为明线,以神教线为暗线,以神器线为辅线而展开的。在游戏进行过程中,主角会见证真实的历史,会体验各地的风俗,会欣赏希腊的风光,会体验人生的波折。奥德赛的世界观即是如此的宏大,在游戏策划的精心编排下将希腊的大观悉数摆在我们面前,同时又让玩家以小人物的视角代入到如画的场景中,如此结合,让玩家一旦进入到角色里,就很难立刻停下来。

-

然而,相比起源把一桌菜一股脑全给你端上来又不介绍菜品让你细细琢磨、品尝,奥德赛更像是古典西餐厅里的一盘盘端上精心调制的菜品并为你介绍菜的制作方式、选用食材、食用步骤,甚至还要告诉你在品尝之前要沐浴更衣精心打扮,这就会产生下面要说的一个问题,大而不精,野心太大但却没有把握住剧情开展的主次,导致剧情上连贯性的断裂,这进而引发一个很严重的问题:游戏进展到后期同质化严重,玩家的游戏热情被大幅度削减。就好比前几道菜都详细介绍,食客会觉得很高端很优秀,但是如果有几十道菜,每个都如此呢?食客就会厌烦。

-

任务

-

剧情是通过任务推进的。任何一个游戏,都有任务,完成了任务,尤其是主线任务,才能顺利推进到下一个剧情节点。可以说,任务是推进主线的首要手段(并非唯一,比如在奥德赛中,满屏幕的问号“?”也是一个手段)。

-

上面我们已经简单说了,奥德赛的主线可以分为三条:家庭线、神教线和神器线。家庭线和神教线是从头贯穿到尾的,二者并行,一明一暗互为依托,这样的设计是很好的:它既不会让游戏快速陷入单线游戏的乏味,也能营造剧情上汹波暗涌的气氛,很快抓住玩家让玩家进入角色。这里值得表扬的是本作家庭线和神教线的融合是非常到位的,二者不但是并行关系,而且是交叉关系,如同螺旋前进的两条线一样,共同推动了游戏剧情的发展。但是,第三条线神器线的出现大大打破了这一节奏,甚至是原本剧情的美感。在家庭线达到关键节点找到母亲后,本来以来父亲的出现会解释玩家所有的疑问,但是没想到,老父亲一出场就让玩家莫名其妙地搜集神器,没有半点前因后果的交代;在找到神器之后,又莫名其妙出现了古代与现代的时空错乱和穿越,我们的主角马拉卡又莫名其妙地活到了现代穿上了西服最终溘然长逝。说实话,我当时玩到这一段的时候几乎是快进过的,没有丝毫欣赏剧情的欲望。这一条线从一开始,到最后,只能用四个字去总结:说的是啥?尽管这一主线流程很短,但它的出现极大割裂了另外两条主线的连续感,也造成了马拉卡精分的错觉,让玩家搞不明白奥德赛的时间线究竟是如何排布的。

-

当然,家庭线和神教线不是没有问题,而且在很多细节之处都有槽点。比如家庭线的一大槽点就是,如果你走的是大团圆路线,你就会发现马拉卡过于圣母(没错,就是我)。诚然马拉卡是一个呆萌渴望家庭温暖外表冷漠内心火热的失足少年,但是当自己的妹妹残忍地杀害基友Brasidas,造了无数的孽后,马拉卡依然选择了原谅她,最后还能和继父、表弟在一个桌子上吃饭,我的内心无疑是充满了问号。你可以说,这是你自己选择的剧情啊!对,但是当玩家选择了这个剧情之后,我们会明显感觉到这个剧情是不尽合理的,也就是说,制作人在设计这一剧情路线的时候就没有仔细推敲其合理性,所以这个锅还要阿育来背。神教线的槽点就更多了,各种奇葩的神教成员(包括那个自爆身份好像老子不怕你的那个)一个接一个,最后鬼魅那段单口相声真的是漏洞百出,竟然还可以选择相信她,而且无论你选择什么,神教线都会到此为止,那又何必再做选项呢。

-
- -
鬼魅的最后一个线索才是“女性”,不然很好猜
-
-

除了主线之外,奥德赛的一大尤为突出的问题是,支线任务过多,而且过于无聊。在奥德赛中,除了标有黄色感叹号的关键支线之外,其他大部分支线都是在端茶倒水、洞穴找人、团灭兵营,基本上可以概括为:“我丈夫/妻子/兄弟/孩子不听我话跑出去不见了/被强盗抓住了,他可能在某某树林/洞穴/兵营里,你能帮我找到他吗”。刚开始玩用来点亮地图确实还不错,而且可以熟悉技能,提高操作,但是当游戏进行到中后期时,满屏幕的感叹号让你没有丝毫欲望去清空,因为你知道打不打都无所谓,对主线毫无影响,给的物品也毫无吸引力。也许制作者的初衷就是让玩家在打完主线之后再慢慢清支线,但实际上,对于一部分强迫症玩家来说,满屏幕没清又强行按捺自己去清欲望的想法的确是非常痛苦的,最终的归宿可能只有隐藏地图标记才能解救他们。尽管对总的剧情没有太大影响,然而过多的冗余支线就好比蒙娜丽莎嘴角沾了米粒,赫拉脸上长满痘痘,人还是那个人,但味道总觉得变了。

-

但另一方面,也不得不表扬一些支线的确做得很有意思。比如“失落的希腊神话”系列任务就很有意思(尤其是“倒霉的一天”),此外,雅典区的一系列支线也很有特色(苏格拉底与雅典炮王的任务),还有一开始瘟疫的支线,这些支线要么本来在内容上非常丰富、有趣,要么会影响后续的剧情,这就回到了支线的作用:需要给予玩家一定的反馈。如果做完一个支线,玩家什么都没有得到,什么反馈都没有,那做这个支线干什么呢?看风景吗?所以,我认为如果把神器线做成一个系列支线,那效果可能就会好很多。如果删掉其中80%的无用的没有任何反馈的支线,说不定奥德赛的IGN评分又会更高。

-

可以看得出来,奥德赛在任务系统上有浓重的借鉴巫师三的痕迹,主要体现在剧情选项和支线系统。支线系统我们已经上面已经说了,和巫师三一样都有很多支线,但是巫师三的支线是它被称为神作的原因,奥德赛的支线只有东施效颦的副作用,究其原因,还是二者对支线作用的理解不同。

-

剧情可选是奥德赛在整个刺客细条系列中的一大突破,也是效仿巫师三最典型的一个要素。为什么我们需要可选剧情?因为这可以给玩家更多的自由度,让玩家能够更好地带入剧情,玩家需要为每一个决定负责,从而就要深思熟虑,剧情选择引发的蝴蝶效应足以在游戏结局的时候升华玩家对整个游戏的评价和感悟。巫师三做到了,奥德赛没做到。这有几点:

-
    -
  • 奥德赛大部分选项不会对后面的游戏剧情有所影响;
  • -
  • 可选项非常少,玩家可能面对哪个都不想选但又不得不选的尴尬境地;
  • -
  • 选择之后的剧情推进和想象的不一样。
  • -
-

从我个人的游戏体验来说,上面三点是奥德赛在“剧情开放”尝试中最为突出的问题,我相信也是大部分玩家承认的问题。奥德赛的最终结局只有几种,而决定这几种结局的选项在几十个小时的游戏中只有寥寥几个地方,那其他的无数选项的作用是什么呢?第二,奥德赛每次可选的大概在3~5个选项之间,而给出的所有选项有时候又都非常智障,明明哪个都不想选却偏要选一个,这对玩家非常不友好,有种强行喂食的感觉。第三,剧情总是朝着意料之外的发展。比如有个任务是关于贩夫的未婚妻,这个时候就不能骗她说贩夫被干掉了,否则直接导致任务失败;而有的时候又需要你假惺惺地欺骗一下NPC才能完成任务,这种任务设定毫无规律可循,遇到了大概率就是一脸懵逼然后呵呵一笑。综上,剧情上的自由可选,就奥德赛的表现来看,反而是禁锢了玩家的自由度,看似让玩家有更多决定剧情走向的权利,但实际上是画地为牢,无论怎么选,都圈定在了编剧预先定义好的条条框框里了。显然,这是一种非常不成熟、宁可没有的设计。当然了,作为向巫师三学习并处于刺客信条系列发展史的一部过渡作品,奥德赛牛刀小试也无可厚非,只是希望英灵殿能够在这上面进行改进。

-

最后来说一下两个DLC。网上普遍对DLC1的剧情诟病颇深,对DLC2倒是赞誉更多,不过我个人倒是觉得,DLC1的剧情高度比DLC2更高,但是下限也比DLC2低,而DLC2发挥整体非常平稳。但从剧情上讲,我个人更喜欢DLC1,尽管它有非常突出的硬伤。

-

DLC1的剧情整体上非常棒,前提你玩的是Alexios(据说Cassandra体验非常不好)。游戏流程不长,十个小时以内可以打完,但是任务节奏很紧凑,内容相对丰富,关键是剧本很给力,讲述了马拉卡遇到真爱但是又被命运无情摧毁的故事。在这个DLC里,我看到了马拉卡内心的脆弱,尤其是他对家庭温暖的渴望与期盼。其实,在玩游戏本体的时候,我们会经常听到马拉卡感慨自己的命运,我印象中比较深刻的是“要是我不离开凯法隆尼亚岛就好了”,再回忆片头马拉卡独自坐在房上抚摸着断矛,他一定非常渴望再见到亲人吧。虽然在凯法隆尼亚岛上,他可能永远也见不到亲人,但是,有Markos,有Phoibe,还有岛上其他的伙伴,这样的小日子,不也很美好吗,正如他与DLC1的女主相遇相知相爱后,与岳父大流士、自己的孩子四人一起生活在村里,平淡而温馨,甜蜜又幸福,不也正是马拉卡一直想要的生活吗。做一个漂流在外四海为家的佣兵并不可怕,可怕的是这样的人却时刻想要有一个稳定幸福的小家庭,这简直是一种无法企及的奢望。当上古维序者干掉女主,马拉卡在火海中呼唤着她的名字时,当马拉卡来到家门口的她的坟墓前时,当马拉卡之后到他们相知的三个地方寻找纪念物时,当最后马拉卡把孩子托付给岳父大流士看着他乘帆远去时,当最后的最后马拉卡仍旧时孤身一人时,我心中的确是被深深触动了。这是我非常喜欢DLC1的点。

-

但是也正如上面所说,DLC1的上限很高,下限也很低,主要仍然体现在细节不到位。比如火海救岳父的情节,作为一个久经沙场的佣兵,他应该是完全能够意识到将妻子儿子丢在船边会有什么后果,就算不能两边兼顾,也应该知道孰轻孰重。再有最后送走儿子的桥段结束得过于仓促,上古维序者这个组织的行为动机也不够充分,在很多地方,玩家是没有办法选择剧情的走向的,也就不得不被迫喂屎,这也是DLC1被广大玩家吐槽的原因所在。但我个人认为,DLC1仍然是瑕不掩瑜的,对于部分玩家而言依旧有非常高的可玩性。

-
- -
这一段还是非常感人的,我流下了130滴眼泪
-
-

相比之下,DLC2的流程就非常非常长了,目前需要30个小时左右才能全部通关。DLC2分为三个章节,每个章节的流程都很长,相互独立但又通过“神话”这一线索串联起来,大概是寻找亚特兰蒂斯并探索神杖的用法。这个DLC把正作的神器线联系在了一起,在希腊和现代两个时间点之间不停穿越,目的就是要给玩家解释这个神器是怎么一回事儿。说实话,我也不知道他解没解释清楚,反正我基本都是快进过了,因为实在是过于无聊。

-

在剧情上来看这个DLC没啥有意思的,我总结一下,第一个章节是神秘四角恋上演姐妹撕逼大战,第二个章节是冥王海王打赌以虐待死者为乐,第三个章节是主角化身正义使者最终颠覆亚特兰蒂斯政权。总之就是云里雾里、莫名其妙。不过值得表扬的是,每个章节的特色地图都非常不错,极乐世界的花花草草,冥界的幽暗阴森感,亚特兰蒂斯的宏伟高科技,给人耳目一新的感觉。我印象最深的还是冥界的Phoibe剧情和Brasidas基友剧情,还是有点感人的,到了最后也特别想痛扁冥王。极乐世界充当双面间谍的感觉也不错,只是亚特兰蒂斯过于平平无奇,除了最后的BOSS有点恶心之外,也没有什么比较让人能够记住的点了。

-

这里特别吐槽一下冥界Brasidas的剧情,实在非常无语和狗血,把编剧的圣母心态体现得淋漓尽致。作为一个从小接受战士教育的将军,在战场杀敌是本分;当敌人杀向你,举起武器反击是本能;只是推开挡路的女人而没有将其杀死,是本心;无论从哪个角度讲,Brasidas都没有做错,战争也好,斯巴达的荣耀也好,还是出于自己的意志也好,Brasidas的做法都是无可非议的。但是,编剧强行要通过这个故事去虐Brasidas,本来都死得那么惨了,还要让他做出痛苦的抉择,实在是非常过分。在这里,我心疼基友三秒钟。

-
-基友太惨了 - -
-
-我恨这个女人 - -
-

画面:时代顶尖,BUG难掩

-

奥德赛延续了起源优秀的画面,在刻画希腊风光上做到了无出其右,以至于获得了“旅游模拟器”的美称。画面其实是一个比较笼统的概念,当我们在说一个游戏的画面的时候,我们一般是在说这个游戏带给我们的所有视觉体验,包括光影效果、水和火的模拟、各种材质的清晰度真实度、整个画面的比例、画面的色彩等等等等,只要是我们能直观感受到的,都可以归入画面里。

-

从画面整体来看,奥德赛做到了时代的顶尖。优秀的光影、逼真的水波、温暖的色调、协调的比例、真实的场景,包括人物的建模等,都非常优秀。最难能可贵的是,奥德赛给我们生动地呈现出了波澜壮阔、风光迤逦、特色鲜明的古希腊景象,尽管可能不是最真实的历史,但是当我们置身其中时,仍然可以感到非常的震撼,仿佛自己就是那个时代的人,经历着相同的事。所以,奥德赛的画面不仅是技术上的时代顶尖,而且他还能带给我们一种沉浸式的体验,这对一个优秀的3A游戏来说,是必不可少的。

-
-光影效果非常不错 - -
-

当然,对奥德赛来说,他还是沿袭了“育碧特色”——各种神奇的BUG,虽然已经比起源好很多了(起源的BUG可以看这里)。比如,你会卡到一个地方无法动弹然后“失去同步”,会在地上皆若空游无所依,会有各种各种严重不严重的穿模。其中最为蛋疼的,其实不算BUG的地方就是,自动寻路。这个简直是噩梦,很多时候系统会绕很大一圈,甚至是本来已经绕了很大一圈了,结果来了个“无法跟随道路”,这是坠痛苦的。不过好在可能是阿育经历了起源雪崩式的BUG,在奥德赛里BUG已经不是一个很严重的问题了,至少可以完成任务不是。

-
- - -
-

人物:形象丰满,行为骨感

-

谈到剧情就不得不稍微说一下人物了。奥德赛对人物的刻画是非常到位的,作为一部史诗大作,奥德赛不仅对主角马拉卡和一些主要人物进行了细致入微的刻画,并且对一些关键配角也有非常到位的描写!

-

首先说一下主要人物:马拉卡、妈和妹妹。马拉卡无疑是最核心的人物了,玩家在关键剧情上的不同选择会呈现出多面的马拉卡形象,由于我选择的是真情圣母路线,所以就按照这个形象说。马拉卡给人的最大印象,在上面已经说了,作为一个铁血真汉子、无情雇佣兵,他的内心其实是非常渴望亲情的,无论是一直心心念念想要找到妈,想要挽回失足妹妹,还是对Phoibe如待女儿一般的呵护与关爱,抑或是对Markos的帮助,都体现了一个流浪在外无家可归的人向往亲情、渴望家庭的内心最真切的期盼。马拉卡不滥杀无辜,同情战争下家破人亡的底层百姓,路见不平也能拔刀相助,时而放荡不羁,时而收敛拘束,就好似武侠小说里的绝世大侠,执剑走江湖,四海皆为家。在游戏的时候,左下角的提示会出现“xx号是你的家,记得常回来看看”(大意如此),不禁让人唏嘘,马拉卡总是在外漂泊,以船为家,在注定不平凡的寻亲之路上经历这么多人生的坎坷,着实让人心疼!另外,马拉卡这个角色还透露着呆萌的气质,一方面是来自配音+动补演员本身的气质,另一方面就是在游戏中,马拉卡的确也很幽默,经常开一些黄腔,引发一些笑话。这些对丰富和塑造人物形象都非常有帮助。

-
-性感吗?我可以 - -
-

然后再说说一些关键配角,这里说几个人:妈、后爸、Phoibe、基友Brasidas、炮王阿尔西比亚迪斯、苏格拉底。这几个人应该是除了主角之外给我留下印象最深刻的几个人了。

-

奥德赛对妈的刻画是非常到位的:坚强独立、能力突出,同时也和马拉卡一样渴望完整幸福的家庭。在找妈的路上,我们已经通过几个支线知道了妈是如何去找妹妹,如何带着她逃跑,又是如何在失去她之后绝望,到她最后重新振作成为一岛的领袖的。在这个过程中,妈的形象跃然纸上,后来和马拉卡一起挽救妹妹更是突出了她追寻亲情的一面。

-

对后爸的笔墨虽然不多,但是也能大致勾勒出一个教子严厉但关怀备至、有些教条但也饱含感情的一个将军形象。作为一个将军,他当初在悬崖边没有办法与神教抗衡,但是他也在一直懊悔。在几个CG里,我们都看出来,尽管他是继父,但是他仍然对马拉卡和妹妹视如己出,教导和关爱也都无微不至。

-

Phoibe和Brasidas应该是马拉卡人生中非常关键的两个人了。一个在他的生活中和他超越了朋友的关系,一个在他的斗争中给予了莫大的帮助。可以说,Phoibe与Brasidas是马拉卡少年时和成年时的两个缩影,一个象征着无邪的天真烂漫,一个象征着成熟的稳重冷静。站在马拉卡的视角,他想要紧紧地把握住他们,不但是对朋友的珍惜,更是对自己这两面的珍视,在他的心里,永远有一个小孩子,愿意陪着Phoibe一起长大,也永远有一个大人,能够和Brasidas并肩作战。

-
- -
马拉卡对Phoibe非常关心,因为她是最亲密的人
-
-

其实说实话,我对Phoibe是又爱又恨的,爱的是她的善良机智,恨的是她的自作聪明,无视马拉卡警告对自己的能力没有清晰的判断,导致自己身死他手,实在让人惋惜。显然,既然作为一个玩家,我已经能够对她产生这样的情感了,那么无疑,奥德赛对Phoibe的塑造也是相当成功的,虽然不那么讨喜,但是至少她是真实的。

-

基友的镜头没有Phoibe那么多,但是当他在贩夫仓库第一次出场时,就注定他有一个悲剧式英雄的解决了。在熊熊燃烧的大火中单挑对方几个好汉,与马拉卡多次在战场上默契配合,在斯巴达时多次帮助主角一家恢复名誉,以至于最后也是战死在战场上的,这样一个人物,实在令人肃然起敬。而且,基友不但是一个优秀的将军战士,他其实也很善良,他在能不杀人的时候是尽量不杀人的,从贩夫,到后来的斯巴达剧情,乃至到DLC的冥界,都体现了基友刚毅且善良的一面。然而,在DLC冥界那里,只要选错一个选项Brasidas就会留在冥界,这个行为这个剧情无非就是编剧强行喂屎表达泛滥圣母心的一面,实在是为这个角色抹黑!

-
-基友真的太帅了 - -
-

最后,炮王阿尔西比亚迪斯和苏格拉底的刻画也是相当有意思。在游戏后半段这两个人都会有很多任务,炮王会不断地让你去帮他约炮,或者和他约炮,而苏格拉底就是不断和你强行抬杠探讨哲学,总的来说,这两个人物的刻画也非常鲜明。其他的包括船长、鬼魅、斯巴达领袖、表弟,甚至是那个性欲很强的老女人,奥德赛对他们的刻画也都有所突出,所谓众生各相,让玩家在走剧情、赏风景的同时还是感受古希腊多姿多彩的人文特色。

-

但是,正如这一节的标题所说的,很多人物的形象是很丰满,但是在一些细节方面,人物的行为动机是严重匮乏的,以至于玩家会觉得“为什么非要这么做”,这样的设计对人物的刻画是有害的。举个典型的例子,亲爸毕达哥拉斯,莫名其妙找到一个神器,然后莫名其妙又觉得自己无敌了,最后又莫名其妙地挂了,过完剧情玩家只会觉得这个人脑子有点不正常,至于其他的性格、形象、心理活动?不不不,脑子有问题就是了。人物行为骨感集中体现在DLC1,这也是DLC1被大家广为诟病之所在。上面已经说过这个问题了,这里不再赘述。

-

最后的最后,在游戏结局时马拉卡与苏格拉底在墓园的对话让人感慨万千,在此摘录如下:

-
-

Alexios:Phoibe,她从没有过小女孩的时光,我总是以成人的方式与她对话。 -苏格拉底:这是因为你尊重她。你可以问问自己为何这么选择,Alexios,但千万不要质疑自己的决定。 -Alexios:谢谢你给了她该有的葬礼。 -苏格拉底:在极乐世界中还有无数的人,这些受到祝福的人已经获得了永恒的喜乐,其中一个就是我们伟大的伯里克利。 -Alexios:基于他的一切作为,全雅典都欠他一份情。 -苏格拉底:我们会称他为“雅典第一公民”不是没有道理的。他是个有很多贡献的人,但也是个孤僻的人。 -Alexios:要是他肯让我们分担他的重担就好了…… -苏格拉底:我们只能从他表现出来的部分来了解他,但谁敢说那就不是他真正的自我呢? -Alexios:这场战争牺牲太多人了,就连布拉西达斯都陨落了。 -苏格拉底:或许如此。但身为一个斯巴达人,他已经尽了自己的义务并战死沙场了。 -Alexios:我当时也有尽全力帮助他。他是我的朋友。 -苏格拉底:你还活着就是他最好的复仇,人们会将他作为英雄来尊敬的。 -Alexios:人们会记得我们是如何打垮克勒翁的。

-
-

类型:转型成功,平衡欠佳

-

起源之前的刺客信条属于传统意义上的ACT游戏,从起源开始,逐渐向RPG转型,到了奥德赛,已经形成了ARPG的初步格局。尽管现代游戏ACT、RPG、ARPG的界限已经趋于模糊化,但是我认为我们还是有必要对游戏进行一个粗略的分类,以更好地对游戏整体的类型风格进行评价。

-

如果用ACT的视角审视奥德赛,那么奥德赛显然是不合格的。这体现在以下几点:

-
    -
  • 正如剧情一节所述,奥德赛非常侧重剧情的讲述,大量的CG、对话、支线任务就是为了铺垫剧情、描绘人物形象,这和传统的ACT游戏淡化剧情是相悖的;
  • -
  • 奥德赛的养成元素非常多,这体现在等级压制、等级上限、装备体系、技能体系上,尽管游戏默认的是等级跟随,但是等级提高带来的技能点收益是非常重要的,尤其在打传奇动物和传奇生物的时候更为显著;
  • -
  • 奥德赛的装备体系决定了它不是一个纯粹的ACT游戏,游戏的蓝装、紫装、黄装品类非常多,并且,极品装备是满属性的紫装,这就意味着玩家可以追求极致一刀99999,尽管奥德赛没有刻意去引导玩家这么做,但是显然,很多玩家已经开始爆肝了。
  • -
-

但是另一方面,我们也不能纯粹从RPG的视角去看待奥德赛,因为游戏尽管整体上非常看重技能,但是如果你没有极品装备而只有黄色套装,那在很多时候,你还是要通过射箭、刺杀或开启无双模型与敌人硬刚,这对你的手法还是有所要求,比如闪避、防反、换装、流派,如果你不想“失去同步”,在高难度下还是需要一定的熟练程度才能手起刀落斩敌人于马下。尽管和起源的防反、马战比起来这都是小儿科,但手残玩家挑战奥德赛的噩梦难度也还是颇有难度的。从这个意义上讲,奥德赛还是保留了一些ACT的元素。

-

其实,当前纯粹的ACT游戏已经是难以走下去了,这是因为游戏作为一种艺术作品,随着时代的发展玩家越发看重它的精神内核。一个游戏可以不要高难度的战斗操作,只要它有足够优秀的剧本,那么它依然可以是神作;但是,一个游戏如果只要求高难度的操作,以及精心设计的关卡,而没有剧情或者足够有说服力的剧情,那么今天的大部分玩家是不会买账的。ACT游戏的RPG化是时代下游戏发展的一个趋势,但对刺客信条这个具有ACT传统的系列游戏来说,如何在这样的趋势下顺利转型是一大难题。

-

就我的体验来说,如果说起源是刺客信条转型的初步尝试,那么奥德赛就是转型的成功探索,它是一次探索,并且整体上成功了。如果你玩过起源和奥德赛,你会发现它们之间有莫名的相似性,但是给你的游戏体验又是完全不同的。上面讲到,起源的剧情是比较破碎的,而在很多地方,你不得不进行战斗,又加之战斗本身的难度,你会觉得剧情反而不那么重要,干掉敌人完成任务这个过程反而才是重点;同时,起源较少支线为主线让路的这个做法本身也是突出战斗的一大考量。如果把起源当成一个ARPG游戏,那么它“A”的成分还是更多一些。但是反观奥德赛,它的战斗就进一步简化了,没有马战,防反简单,刺客的飞雷神简直开挂,后期技能及其变态,甚至凑齐极品紫装后就是一刀一个小朋友,战斗本身就成了服务剧情的工具,玩家玩得爽就完事了。但是,仍然作为刺客信条系列游戏的奥德赛,总不能全是过剧情吧,那怎么办,疯狂增加要塞、兵营、洞穴就好了!再给你设计无穷多的支线,每个支线都让你去打怪,难度不够,数量来凑,这不动作的成分一下子就高上来了。

-

这是一个简单且聪明的想法,但对于奥德赛的定位来说,不够明智。我们可以体会到制作人想要保留APRG的动作成分和角色扮演成分,并且都要做大做强。这当然是可能的,如果做的时间足够久。但是,对于奥德赛来说却不可能,理由如下:

-
    -
  • 开发时间短。尽管实际的开发时间我们不能精确得知,但是奥德赛发行于2018年10月,它的上一作发行于2017年10月,两作中间仅间隔一年,是标准的年货游戏。虽然奥德赛是魁北克工作室开发的,而起源是蒙特利尔工作室开发的,但是二者在整个系列的表现形式上具体显著的承接关系,故姑且认为奥德赛的实际开发周期约为2年,这个时间对3A游戏来说不算长;
  • -
  • 奥德赛的定位。奥德赛位于刺客信条系列从ACT向PRG转型的过渡阶段,是比起源更具标志性的一部作品。我个人认为,奥德赛的定位就是一部不成熟的ARPG过渡作品;
  • -
  • 经验的缺乏。显然,阿育对如何结合刺客信条系列传统的ACT元素与RPG元素的经验还有所缺乏,在短时间内要做一部顶级APRG游戏是非常困难的。记得是谁说过,“奥德赛就是想让你玩两年,直到下一部作品问世”,于是,他们把奥德赛的内容强行用问号、感叹号去填充,让玩家去爆肝刷装备,然后体验一刀99999的快感,但实际上,这种大而空的填鸭式内容对大部分骨灰级单机游戏玩家都没有吸引力,有的只是机械重复式的屠杀敌人,毫无乐趣。
  • -
-

所以说,在奥德赛的游戏类型上(ARPG),平衡性是不够优秀的。大到动作成分与角色扮演成分的平衡,小到装备的平衡、技能的平衡、数值的平衡等多方面,奥德赛的“全都要”战略俨然对大部分玩家来说只是一个噱头。如果只把奥德赛玩成体验剧情、模拟旅游,那奥德赛是再好不过的,可是一旦要玩操作、玩养成,奥德赛好像就走向了低配版的国产网游之路。游戏类型的平衡,是奥德赛的一大不足。

-

总结:育碧特色,系列之巅

-

总的来说,和以往的刺客信条年货作品不同,奥德赛算得上是一部精心打磨的作品,无论是剧情、画面,还是人物、音乐,抑或是育碧特色BUG,都看得出来是经过仔细调教了的。我个人认为奥德赛是综合看来刺客信条系列的巅峰之作,IGN评分9.2也算是比较中肯的评价了(对比起源9.0和巫师三9.3的评分)。如果要我来打分的话,我会打一个9.0分,要是阿育能够在剧情上再多打磨打磨,就可以给到9.5分。如果要说和巫师三差在哪儿,就差在剧情上。

-

另外,在本文中我没有谈到奥德赛的音乐,但这并不意味着它不出色,相反,我非常喜欢奥德赛的配乐,对比起源阴间的配乐,奥德赛的整体音乐风格就显得非常阳光和带感。OST可以在B站或者QQ音乐搜到,我已经循环很多天了,强烈推荐大家也去听一听。

-

对于11.10号即将发售的英灵殿来说,我个人还是非常期待的,一是可以亲手操作猛男,二是从试玩片段来看,它革除了奥德赛中饱受诟病的一些要素(比如剧情、满屏幕的问号、又臭又多的支线,过于强调等级和装备等),同时还保留了优良的画面。从这两点来看,英灵殿无疑是让人期待的。不管你预卜预购,我反正是已经预购了(还是等打骨折更划算一些/(ㄒoㄒ)/~~)

-]]>
- - 游戏 - 玩后感 - - - 随笔 - 游戏 - 生活 - 刺客信条 - -
- - 《Assassain's Creed Valhalla》玩后感 - /2020/11/12/23/23/ - 刺客信条:英灵殿(Assassin's Creed -Valhalla,下称英灵殿)无疑是刺客信条“神话三部曲”中最成功的一部,无论是从游戏的质量上,还是从销售成绩上。在80个小时的主线与10多个小时的“内置DLC”游玩后,我个人对英灵殿的评价可以总结为“自我突破,承前启后”。说它自我突破是因为它革除了起源和奥德赛中的绝大部分缺点,在自我的改进中完成了里程碑式的跨越;说它承前启后式因为它标志着刺客信条系列一次华丽而成功的转型与过渡,是游戏特色与市场充分交合后的最佳发展方向,是游戏缺点自我祓除与优点渐进式凸显的结果。本文将重点从游戏的任务系统、剧情节奏和人物刻画这三个方面介绍英灵殿的蜕变,至于游戏画面和风景,还有育碧特色BUG,都是刺客信条系列经典的历史特征了,在本文不再赘述。

- -

任务系统:众星捧月映空青,稀光散落满夜明

-

剧情节奏:一马平川驰此间,峰峦叠嶂牛羊见

-

人物刻画:穷极千里观百态,瓦泥滂沱照众生。

-

总结:长路漫漫终将至,共邀朝阳行远方。

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-]]>
- - 游戏 - 玩后感 - - - 随笔 - 游戏 - 生活 - 刺客信条 - -
- - 《电脑游戏:文本、叙事与游戏》纪要 - /2021/08/14/18/08/ - 本文是《电脑游戏:文本、叙事与游戏》的简单纪要,在此备份。

- -

游戏与叙事

-

叙事理论

-
    -
  • 每一个叙事都有两个部分:一个是故事,指事件的内容或串联(动作、发生的事),再加上所谓的存在物(角色、场景中的事物等);二是话语,即内容赖以传达的方法。
  • -
  • 故事的事件在时间和空间上都是按照一定的顺序排列的,这就是所谓的情节设计
  • -
  • 叙事事实的三个方面:故事(内容)、叙事(话语)、叙述。 -
      -
    • 童话《灰姑娘》中包含了一系列故事的事件,可以通过很多种不同的方式被呈现出来。
    • -
    • 叙事的关键要素:对事件加以安排,使之在话语当中凸显出来,对它们的时间长度、发生频率和次序予以控制。
    • -
  • -
  • 隐含作者与隐含读者。
  • -
-

叙事理论与《博德之门》

-
    -
  • 游戏包含了一系列由玩家所触发的新事件,允许玩家实时行动;也包含了过去的事件,已经被安排好的。
  • -
  • 事件被提前预制好,玩家是做出情节安排的人。
  • -
  • 同步叙事:向玩家传达屏幕上的行动、事件、得分和对话。
  • -
-

叙事的限度

-
    -
  • 叙事理论追问的是:故事情节是从何种角度被描述的,我们是通过谁的眼睛了解叙事当中的事件的,又是谁的声音在传递信息,是谁在策动事件,谁来决定事件的持续时间、发生频率和次序。
  • -
  • 《博德之门》以一种玩家无法操纵的形式讲述事件,以过场动画、NPC的对白和角色简介的方式被编织在一起,并与玩家发生联系。
  • -
  • 已然事件实时事件
  • -
-

游戏与快感

-
    -
  • 电脑游戏究竟要驱使玩家去做什么。
  • -
-

角色的生成

-
    -
  • 选择外观、种族、职业、阵营、声音、名字。通过这些选择,让玩家了解游戏玩法与背景,同时限定了一系列数值。
  • -
-

角色、特征和游戏

-
    -
  • 角色既包含了审美层面和故事层面的偏好,也涉及战略方面的考虑。人物的特征会影响(约束)其行为。
  • -
  • 但是玩家的风格和喜好决定了角色内在的可能性以怎样的形式表达出来,或怎样被压抑。【如果想要增强角色自身的特征,应该限制玩家的可操作性,如从数值、剧情、环境、探索度等层面。】
  • -
  • 在电脑游戏中,玩家的input和游戏所提供的特征描述之间的互动可能引发不可预测的行为,与某个角色表面的扁平性发生矛盾。因此,仅仅按照设定的属性和特征去描述一个游戏化身或角色是错误的。【可以通过“如果不这么做,游戏就难以进行下去”的思路引导玩家进行特定的操作。】
  • -
-

沉浸、卷入与“心流”体验

+

Midjourney不完全使用指南

+

太长不看版:使用规范

+
    +
  1. 首先根据公式 Prompt = 构图说明 + 画面内容 + 美术风格 + +光影设置 + 其他描述 + 参数列表 按照下面的模板写Prompt:
      -
    • 玩家将游戏系统看做一个整体,但是在这一游戏系统中,玩家还必须关注一系列次级系统,那就是角色的行动、武器、物品和技能。
    • -
    • 在文学研究中,沉浸感是指读者所享受到的放弃批判思考的全神贯注。认知心理学的图式理论使我们能够感知周遭的事物并进行有效的处理,这一能力有赖于此前通过阅读、个人经验和别人的建议而建立起来的对类似事件的已有知识储备。【也就是说,沉浸感实际上是一种记忆唤醒机制?】
    • -
    • 只要故事、背景和界面保持统一的图式,玩家的审美体验就能在很大程度上保持沉浸感。
    • -
    • 卷入,是一种经过更多思考的批判式的参与方式。 +
    • 人物特写:a {headshot | closeup} portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • +
    • 人物半身照:a {medium shot | over the shoulder shot | head and shoulder shot} portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • +
    • 人物全身照:a full body portrait of [content], [art style], [lighting], [other keywords], [MJ parameters]
    • +
    • 风景/建筑照:[content], [art style], [lighting], [other keywords], [MJ parameters] +上面用大括号{}括起来且用竖线|分割的内容是多选一,即从所有可选的关键词中选一个即可。用中括号[]括起来的是我们需要根据画面内容填写的。下面介绍中括号填写的基本规范:
    • +
    • [content]:是我们要填写的画面内容。如果是人物,主要填下面的内容(不一定都需要,也不一定完整,根据你的需求):
        -
      • 当读者面临不熟悉的题材,或者文本很难左右其反应时,他们被驱使着一再重读或反复斟酌其中的信息。
      • +
      • 什么人:{boy | girl | woman | old lady | warrior | witch | anthropomorphic white tiger | ...}
      • +
      • 脸部特征:{beautiful face | dedicate facial features | colorful tattoos on her face | ...}
      • +
      • 表情:{smile | crying | moaning | angry | ...}
      • +
      • 眼睛:{beautiful blue eyes | shining diamond eyes | ...}
      • +
      • 头发:{long curly black hair | flowing hair | braided hair | ... }
      • +
      • 装饰:{exquisite garland | opal decorations | wearing feather headdress | ...}
      • +
      • 穿着:{wearing tall black high heel boots | in purple and white kimono | ...}
      • +
      • 其他细节:{made of flower | rain on her face | ...} +如果是非人物,则一般不需要写这么细,只要描述大概是什么物体就好了。比如magnificent mountain, +interior of a cyber punk city等等。
    • -
    • 沉浸的快感来源于我们伴随某种熟悉图式的退潮和涌动而完全专注其中。卷入的快感则来自我们从文本之外的视点去识别一件作品如何将互相矛盾的图式翻转、连接。【“相互矛盾”可以是新的情节、新的人物、新的动作,总之与既有的图式产生了区别之处。】 +
    • [art style]:指定艺术风格,可以通过三种方式指定:
        -
      • 当屏幕上的场景要求即时的、固定的反应时——比如思考、凝视、阅读、击打、探索、行走——就促使玩家进入沉浸状态。
      • -
      • 当玩家不得不与屏幕上的行为逐渐拉开距离时,就进入卷入的状态。可能是由于角色的特性、一条迷失的小路、一个强大的敌人等。这时候就要寻求解决之道:重复行为——>重新经历——>查找攻略。
      • -
      • 沉浸和卷入相互依赖,玩家在流动性和安逸心情之间切换,游戏世界由此塑造体验。【战神的战斗、探索、叙事是一种沉浸,而其中的解密、情节、道具、新的敌人又是卷入。不断创造卷入,然后将卷入转化为沉浸,再创造新的卷入。
      • +
      • 直接指定主题/绘画风格:比如cyber punk, +steam punk, japanese anime, +oil painting, realistic painting, +ink painting, ukiyo-e, +bookstory illustration等等;
      • +
      • 直接指定相关风格的艺术家:比如吉卜力风格可以说art by {ghibili | koji hoshino | hayao miyazaki},皮克斯/迪士尼风格可以说art by {pixar | disney},等等;
      • +
      • 直接指定作品:比如breath of the wild, +dark souls, world of warcraft, +lord of the rings等等。 这里推荐使用第二种方式。
    • -
    • 随着游戏难度的提高,玩家的应对能力也在进步,游戏需要与玩家的水准相匹配。这时候玩家总说自己“处于巅峰状态”。【战神。】
    • -
    -

    空间、导航与情绪反应

    -

    《异域镇魂曲》与《寂静岭》

    -
      -
    • 《寂静岭》的音效使之恐怖感上升。
    • -
    • 《寂静岭》的线性结构能让游戏始终保持一定的节奏和紧张感
    • -
    -

    导航:迷阵、根茎与迷宫

    -
      -
    • 空间导航有两种模式:迷阵与根茎。 +
    • [lighting]:画面的光影效果,一般来说使用{cinematic lighting | volumetric lighting}比较通用。但对人物,你偶尔还需要用{back lighting | rembrandt lighting | spotlight};对非人物,你可能还要{morning lighting | flare}等等。
    • +
    • [other keywords]:其他关键词包括:
        -
      • 迷阵意味着在向唯一的出口有条件地前进,缺点是玩家只能被引导到预先设定的唯一出口。
      • -
      • 根茎并没有特定的方向比其他方向更有利,缺点是结构的缺失。
      • -
      • 游戏当中的迷宫应当处于二者之间:要实现的目标应足够强大,引导玩家前进;同时设定开放式结局,令玩家可自由探索。【巫师三和BOTW的一大共性(实际上Sekiro也是):主线分支化,鼓励玩家自主选择、探索。核心要点是:自由地引导。】
      • +
      • 材质:{cubic | bronze | wood | liquid | glass | prism | smoke | plume | milky way | ...}
      • +
      • 色彩:{dark pink | rainbow | vibrant | warm color | black and white | monochrome | ...}
      • +
      • 形状:{star | torus | polygonal | low poly | interior | stellation | stellation | ...}
      • +
      • 其他一般默认加上用来叠BUFF的词:{intricate details | 8K | enchanting | masterpiece | octane render | unreal engine 5 | well composed | award winning | high resolution | ...}
    • -
    • 恐怖效果地生成总是基于“一个简单而明显地基本公式:怪物使常态遭到威胁。” +
    • [MJ parameters]:Midjourney要填写的参数,一般使用下面的参数组合(但也要有意识地灵活运用):
        -
      • 《寂静岭》中,常态被怪异之物威胁并摧毁。
      • -
      • 《异域镇魂曲》中,怪物本身就是常态。
      • +
      • 人物特写照:{--ar 1:1 | --ar 3:4} --q 1.5
      • +
      • 人物半身照:{--ar 3:4 | --ar 9:16} --q 1.5
      • +
      • 人物全身照:{--ar 2:3 | --ar 9:16} --q 1.5
      • +
      • 非人物照:根据你的画面内容选择宽高比,如果是横板,则用--ar 16:9 --q 1.5;如果是竖版,则用--ar 9:16 --q 1.5;如果是加长竖版,则用--ar 9:32 --q 1.5
    • -
    -

    游戏文本与遍历函数

    -
      -
    • 呈现于受众面前的称为“脚本单元”:是不同玩家将游戏中的信息“玩出来”的。
    • -
    • 存在于文本之中的称为“文本单元”:是游戏提供的所有潜在信息。
    • -
    • 使脚本单元自文本单元中显露或生成并呈现于玩家面前的机制,叫做“遍历函数”。
    • -
    • 遍历函数的几种变体:活跃性、可确定性、可访性、连接性。 -
        -
      • 活跃性(与文本单元和脚本单元的数量与稳定性有关):《异域镇魂曲》比《寂静岭》更加活跃少停滞,因为前者任务更多,且一部分是强制性的,而且文本单元更丰富,玩家选择和组建的脚本单元的可能性也都更多。
      • -
      • 可确定性(给定状况相同反应是否总是导致相同的结果):《异域镇魂曲》比《寂静岭》更加不确定。
      • -
      • 可访性(玩家是否能在游戏中任意一点进入游戏文本):《异域镇魂曲》比《寂静岭》可访性更强。
      • -
      • 连接性(文本单元间时空的连接):《异域镇魂曲》比《寂静岭》连接性更强。
    • -
    • 《寂静岭》更加稳定、有节制、更具确定性的特点塑造了它迷阵般的结构。《异域镇魂曲》较富于开放性的遍历模式则表现出如根茎般的扩张趋势。
    • -
    -

    替身

    -
      -
    • 沉浸分为感知层面的沉浸和心理层面的沉浸。 +
    • 写好之后,多次出图,你也可以尝试几次参数--test --creative--testp,一般来说效果会更好些,但注意风景图不要用--testp
    • +
    • 从所有的图中找到让你比较满意的图,使用UpscaleVariantRemaster功能对它们增强,反复出图;也可以继续在Prompt中增删细节,直到满意为止。
    • +
+

关键词参考工具
+更完整的关键词参考
+艺术家参考
+风格参考 +其他人的作品参考1 其他人的作品参考2

+

概述

+

Midjourney是当前最流行的AI绘画工具之一,它部署在Discord上,因此你需要注册一个Discord账号才能使用。

+

所有的AI绘画工具最重要的就是如何写Prompts,也就是文本描述。在开始之前,你需要知道写Prompts的几个基本准则:

    -
  • 感知层面:游戏体验垄断了玩家的感觉,如听觉、视觉。
  • -
  • 心理层面:玩家全神贯注地投入到想象中的游戏世界去。
  • -
-
  • 《异域镇魂曲》的等距视角和游戏主角本质上的复合性在感知和行为两方面都大大消解了玩家与化身的同一感(削弱了感知层面的沉浸)。但游戏以另一种方式令玩家沉浸其中:大量描述性文本的阅读,以及关于玩家对游戏主角、其作战团队及其物品的巧妙运用的细节信息的缓慢积累。【古剑三】
  • -
  • 《寂静岭》是感知层面的沉浸,各种即时状态。
  • -
  • 《异域镇魂曲》拒绝被匆忙对待,而《寂静岭》催促着玩家从一个地方到另一个地方,抵达终点。
  • +
  • 详略得当:描述越详细,图片越有可能接近你想的画面,但是也有更大的概率生成的图片质量更低;描述越简略,图片越多样化,质量也可能更高。但注意不要加太多细节,否则会图片会很低。一般来说,我们只需要写”意象“,而不要写得过于具体。
  • +
  • 以短代长:少用超过10个词的句子,而用多个短语,每个短语描述画面的一个细节/部分/风格。即使要用长句,也不要太长,保持在20词以内。
  • +
  • 反复润色:不可能第一次生成的图片就完全符合你的想象,需要不断给Prompts润色修改,这不是一个简单的活,因此请保持耐心。
  • +
  • 具象描述:尽量用一些具象的名词、形容词,比如river, +rockstar, Zeus, landscape, +happy, +dark等等,不要用一些难以在现实中找到对应实体的词,比如knowledge, +notion, type等等。
  • +
  • 指定量词:显式指定对象的数量,如果是一个就用a,如果是多个就指定具体数量。
  • +
  • 描述风格:在多数情况下都需要增加风格关键词,比如cyberpunk, +surreal, abstract, +realistic,也可以指定一个或多个艺术家,比如hiroshi yoshida, +Max Ernst, MC Escher, +Yoji Shinkawa等。此外,你还可以指定具体的绘画形式,比如sketch, +woodblock print, oil painting, +watercolor painting等等。
  • +
  • 描述构图:可以显式指定构图,比如a portrait of, +an ultrawide shot of, a headshot of, +a closeup of等。
  • -

    (更多细节,侧重代入感,带来感知沉浸)小<---------视距--------->大(更多信息,侧重策略性)

    -

    角色扮演

    -

    社会符号学视角

    +

    MJ官方文档:https://midjourney.gitbook.io/docs/

    +

    注册账号

    +

    Midjourney当前作为Discord的内置服务,你可以按照下面的步骤注册账号开始使用:

    +
      +
    1. 登陆官网,点击Join the beta: +
    2. +
    3. 进入后输入昵称,加入Discord,如果你没有discord,可能需要根据提示注册一个,之后进入服务器: +
    4. +
    5. 进入一个以#newbies开头的频道,比如#newbies-117:
    6. +
    7. 在下方的输入框中输入/imagine,此时就能在弹出来的prompt框中输入你想要生成图片的文本描述了,比如我这里输入的是a +white flower is crying,稍等片刻,就能在聊天框中看到生成的4张图像了: +
    8. +
    9. 除了生成的4张图像外,下方还有两行按钮,分别是U1/U2/U3/U4和V1/V2/V3/V4,分别表示增大每张图的分辨率,以及为每张图重新随机生成。在点击增大分辨率之后,对应大图会重新发送在频道中,下方也会随之出现几个新按钮,见字如义: +
    10. +
    11. 如果你不想在公共频道,你也可以自己创建一个频道,然后邀请Midjourney +Bot到你的频道中。首先在左侧点击添加服务器;然后创建一个私有服务器;最后回到Midjourney的官方服务器,找到Bot,点击后把它添加至服务器即可。 +
    12. +
    +

    然后你就可以在你自己的服务器里愉快地玩耍了!

    +

    使用教学

    +

    首先记住下面的公式:

    +

    Prompt = 构图说明 + 画面内容 + 美术风格 + 光影设置 + +其他描述 + 参数列表

    +

    其中,“构图说明”也可以放在“美术风格”后面,但一般来说直接通过a portrait/closeup/wide angle shot of ...指定了。除了“画面内容”是必须的之外,其他的都可以省略。

    +

    建议初学者在这个网站这个网站找对应的关键词,多做尝试。

    +

    构图说明

    +

    构图说明指定是怎样的构图,比如特写、近景、远景等等。有下面基本的构图: +- 特写: closeup, portrait - +全身照:full body, full body portrait - +风景:wide angle, epic composition, +low angle, high angle

    +

    Prompt一般直接用a [composition] of ...开头,其中[composition]就是你选择的构图,比如你想要一个特写,你就可以说a closeup shot of ...或者a headshot portrait of ...;如果你想要一个全身照,你就可以说a full body portrait of ...

    +

    对于风景图,一般不用上述格式,而是直接以内容开头,把构图放在后面,比如vast grassland, wide angle, epic composition,首先说明内容是草原,然后再说用广角镜头和宏大构图。

    +

    下图分别是特写/中景/远景的例子,Prompt为a [composition] of an old asian lady --ar 3:4 --q 1.5,其中[composition]分别替换为closeup shot, +medium shotfull body portrait,同时把宽高比分别设置为3:4, +2:39:16。最后一张图是风景图,Prompt是vast grassland, wide angle, epic composition --q 1.5 --ar 32:9

    +

    +

    你可以看到几种构图之间的差别,至于为什么要更改宽高比,详见下面的参数列表。

    +

    画面内容

    +

    画面内容指定画面内容。该部分根据需求可详可略,但一般都以多个短语组成,比如下面我想以凤凰为原型设计一个角色,全身照,有红色和黄色的花,穿着彩色华丽的装饰,因此输入的Prompt为a full body portrait of a phonix goddess, red and yellow blossoms, wearing rainbow opal accessories, exquisite decorations --ar 9:16

    +

    +

    第一张图加了参数--test,因此细节更加丰富。

    +

    对于非人物也是相同的,比如我现在想设计一个亚特兰蒂斯城,它矗立在悬崖边,有着豪华的建筑,我就可以用the city of Atlantis on steep cliff, enormous beautiful palace, exquisit architecture --aspect 9:32 --q 1.5,得到下面的图:

    +

    +

    前两张图用了--test

    +

    再次强调:描述内容的详略会极大影响生成的结果,越详细,生成的图片会越接近你想象中的画面,但有更大概率质量更低;越简略,越有可能生成非常酷的图片。因此,是否详略取决于你在脑海中是否已经有一个大致的画面,如果你完全没有想法,请尽量保持简略!

    +

    比如对我想要的凤凰角色,我不知道她具体是什么样子的,就只需要输入a full body portrait of a phonix goddess --ar 9:16就可以了,然后再不断添加细节(前两张图是原始Prompt,第三、四张图增加了red and yellow blossoms):

    +

    +

    美术风格

    +

    美术风格指定图片的美术风格是怎样的。美术风格非常重要,它直接决定了图片内容是否与你想象中的相符。我们可以通过三种方法指定美术风格:(1)绘画风格,如realism, +realistic, abstraction, +impressionism, oil painting, +cover art, +comic book等等;(2)艺术家名字,如Rolf Armstrong, +Lois van Baarle, +Aubrey Beardsley等等;(3)与该风格有关的作品/游戏,如breath of the wild, +genshin impact

      -
    • 图像语法:再现性(再现这个世界)、互动性(允许文本的作者和读者之间的沟通,以及文本中虚构人物与读者之间的沟通)、组织性(使文本各要素连贯一致,由此组合起来以进一步实现它们承载的含义)。
    • -
    • 多模态理论:文本如何将不同的符号模态——如讲话、书写、声音和图像——组合在一起,游戏是多模态的文本,因此应该思考,动画、视觉设计、音乐、文字文本和声效等多种沟通模态的联合是如何实现前述三个主要功能。
    • +
    • 指定绘画风格:比如现在我想对上面的凤凰角色风格化,我可以指定不同的绘画风格,比如下图是依次指定为realism, +abstraction, watercolor painting, +oil paintingcartoon, anime的结果:
    -

    克劳德——大英雄

    +

      -
    • 克劳德这样的角色在相当大的程度上利用了民间文化、口头叙事和罗曼司等传统形式,通过精心构建的寓言故事,为日常生活中的过渡仪式和苦难提供了情感慰藉、道德争论和心灵拷问。
    • -
    • 电脑游戏是被翁称为高科技社会“次级口述”的案例之一,它是口述文化这种思维模式的进化,基础是文学和以技术为媒介的文化。
    • +
    • 指定艺术家:相比指定风格,一个更好的方法是直接指定艺术家,比如我依次指定了下述艺术家Alphonse Mucha, +Alyssa Monks, Andreas Rocha, +Miyazaki HayaoEric Lacombe,所生成的图片是:
    -

    克劳德——数码傀儡?

    +

    +

    你也可以指定多个艺术家,但最好它们风格相似。你可以在这个表里找到一些参考艺术家。

      -
    • 将克劳德这样的文本建构视为一种固定的对象却可能导致我们忽视玩家—化身这一重联系,甚至是忽略普遍意义上的文本的含义。
    • -
    • 玩家同时也是一种文化资源,是阐释者,也是粉丝艺术和粉丝写作当中对游戏资源进行改编的人。
    • +
    • 指定相关作品:你还可以显式指定作品,下面的图依次显式指定了作品naruto, +breath of the wild, dark soul, +genshin impactminecraft
    -

    化身的操纵

    +

    +

    一般来说,推荐直接指定艺术家,辅之以绘画风格和相关作品,注意这三者之间的风格要尽量保持一致。当混用的时候,艺术家放在前面。

    +

    光影设置

    +

    图片的光影也是重要的一部分,我们可以直接指定光影的类型。比如我们以vast grassland with a lake in the center, a giant tree growing by the lake, --ar 16:9 --q 1.5为基础Prompt,分别考虑下述光影moody lighting, +morning lighting, cinematic lighting, +soft lighting, volumetric lighting, +rembrandt lighting, +godrayschiaroscuro

    +

    +

    除了风景图之外,人物也可以应用不同的光影。下面以a full body portrait of a phoenix goddess, red and yellow blossoms, wearing rainbow opal accessories, exquisite decorations --ar 9:16 --q 1.5为基础Prompt,同样依次加入上面的光影设置:

    +

    +

    可以看到,光影能够影响画面的整体风格,因此,根据内容选择一个合适的光影至关重要。

    +

    其他描述

    +

    除了上面的构成要素外,你也可以增加其他你想要的关键词,大致可分为下面几类。

    +

    材质

    +

    材质(Material/Texture)也可以用来描述画面的整体风格和细节,比如cubic就可能会使画面出现方块状物体。

    +

    下面以a beautiful moon above the desert, the moon is in intricate details, marvel cosmic, Cory Loftis, Conrad Roset, epic composition, low angle, dramatic lighting, spotlight, greyscale, cubic, [material], psychedelic, 8k --ar 2:3 --q 1.5为基础Prompt,分别使用材质cubic, +bronze, carbon fiber, foil, +glass, wood, +liquidsmoke, plume

    +

    +

    可以看到,加入不同的材质会整体或局部地影响画面。carbon fiber使画面增加了颗粒感,glass让月亮出现了玻璃状物体,smoke, plume使得画面出现烟雾。当然这里由于Prompt前面的内容足够丰富了,导致部分材质的影响较小,所以区别不是很明显。

    +

    如果用简单的描述,再搭配材质关键词,效果会更明显些(Prompt为a tree, [material] --ar 9:16):

    +

    +

    颜色

    +

    在Prompt增加一些与颜色有关的关键词有助于生成你想象中的画面。最简单的就是直接添加颜色词,比如red, +black, +blue等等,但这样效果不一定好。一般来说,我们可以增加带有色彩意向的词,比如rainbow, +vibrant, warm color, prismatic, +black and white, monochrome, +high contrast等等。

    +

    下面以a medium shot portrait of a beautiful women in dark green kimono, beautiful face, smile, blue eyes, long black hair, painted by Anne Stokes, rembrandt lighting, [color], ultra detailed, plume --ar 2:3 --s 5000为基础Prompt,分别以vibrant color, +prismatic, black and white, +monochrome, +colorful, rainbow为颜色关键词:

    +

    +

    形状

    +

    你还可以添加形状关键词。这个形状不一定是常见的三角形、正方形,也可以是跟形状有关的物体,比如金字塔pyramid, +星星star,心形heart等等。

    +

    比如以a mountain, [shape] --ar 9:16为基础Prompt,考察下述形状star trapezohedron, +star prism, torus, polygonal, +polyhedron, interior, stellation, +square, heart, gear

    +

    +

    polygon(多边形)是一种常见的风格,interior则会绘制物体的内部。

    +

    其他

    +

    一些其他对画面有帮助的词包括: - +细节程度:very detailed, spectacular details, +ultra detailed, intricate details - +清晰度:4k, 8k, high definition - +景深:depth of field, Canon 50mm - +情绪:enchanting, impressive - +气氛/环境:vintage, retro, +cosmic, celestial, seaside, +lucid dream, plume, Gossamer - +绘法:spatter, drips

    +

    你也可以增加其他的意象词。

    +

    参数列表

    +

    你可以在Prompt的最后添加一些参数,用于生成你想要的图片风格和质量。下面列出所有参数,其中加粗的是最常用的。

    + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数功能
    /imagine呼出prompt,根据文本描述生成四张图片
    /info查看当前正在运行的任务
    /fast(/relax)切换为使用Fast/Relax GPU时间
    /private切换为private模式,其他人不可见你的图片
    /public切换为public模式,其他人可见你的图片
    --hd使用旧算法,适用于抽象和风景图,图片分辨率更高
    --ar 显式指定图片的宽高比,比如 --ar 16:9
    --w 显式指定图片的宽度,比如 --w 320
    --h 显式指定图片的高度,比如 --h 256
    --seed 显式指定种子数
    --no 生成的排除该关键词,比如--no +plants为去掉文本中的”plants”
    --iw 设置prompt中的图片/文本权重比,默认0.25
    --s 指定生成图片的风格化程度,值越大,图片越“抽象”,默认为2500
    --q 指定图片质量,默认为1,值越大,细节越多,但耗时越长
    --chaos 指定图片的随机性,值越大,生成图片越多样,范围[0,100]
    --fast更快地生成图片,但质量会更低,近似于--q +0.5或--q 0.25
    --stop 在n%的时候停止终止生成
    --uplight在Upscale的时候用light版本,增加更少的细节,与原图更接近
    --testp生成更接近现实的图片
    --test生成更多样化、风格化的图片
    +

    指定宽高比:--ar

    +

    --ar指定了生成图片的宽高比,默认为1:1。宽高比会极大影响所生成的图片,比如下面的例子(基础Prompt为Utah teapot, wood --seed 1,从上到下、从左到右分别是宽高比为1:1,2:3,4:9,4:16,3:2,16:9,9:4,16:4): +

    +

    可以看到,对于同样的内容描述,宽高比直接影响所生成的内容,这是因为AI默认会“填满”整个图,所以在设定宽高比时,要注意你要生成的内容是怎样布局的。

    +

    指定风格化程度:--s

    +

    -s指定了图片的风格化程度,或者"天马行空度"。默认值为2500;20000会让你的图片看起来比较抽象,但也没有完全偏离你的prompt;但是60000会让图片完全无视prompt自由发挥。

    +

    下面举几个例子说明(基础Prompt为Utah teapot, wood --ar 16:9 --seed 1,左边是2500,中间是20000,右边是60000):

    +

    +

    可以看到,--s 20000时图片的某些部分已经不符合输入的Prompt里,比如这里丢失了木头材质的信息;--s 60000时就开始放飞自我了。

    +

    对于这个参数,一般来说保持默认即可,如果你想要更多样化的结果,可以用5000~10000之间的值。

    +

    指定细节度:--q

    +

    -q指定了图片的质量,也就是细节的丰富度。默认值为1;2会让图片细节更加丰富,但生成速度也是原来的一半;5会让图片细节爆表,但也有可能导致图片整体效果很差。

    +

    下面举几个例子说明(基础Prompt为Utah teapot, wood --ar 16:9 --seed 1,左边是0.25,中间是1,右边是2):

    +

    +

    可以看到,图片的细节度是递增的。当然这个例子过于简单了,导致细节度高的茶壶反而有点奇怪。

    +

    这个参数我比较喜欢用--q 1.5,谁不喜欢更多细节呢?

    +

    生成更逼真/风格化的图片:--testp, +--test

    +

    --testp让生成的图片更加逼真,而--test会让图片更加风格化。注意这二者都会只输入一张图片而不是通常的四张图。

    +

    下面有个例子(基础Prompt为Japanese house with pink roof --ar 16:9 --seed 1,左边为--testp,右边为--test):

    +

    +

    左侧的房子很逼真,右侧则不完整。当然这个例子可能不够好,但足以说明这两个参数的区别。

    +

    值得注意的是,并不是增加了--testp生成的图片就一定是更现实的,但一般而言是更逼真的。比如你想生成一张二次元萌妹,加了--testp之后反而可能会让萌妹更加仿真,虽然我们都知道她不是现实中存在的。

    +

    加入参考图片

    +

    除了纯文字内容外,Prompt还支持插入图片,让生成的图片在内容和风格上参考给定的图片。

    +

    要插入图片,只需要把图片的链接放在Prompt开头就可以了,比如: +https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Utah_teapot_(solid).stl/1200px-Utah_teapot_(solid).stl.png Utah Teapot --ar 16:9 --seed 1 +这个Prompt最开始的链接就是图片地址,然后就是常规的文本内容,把原图和生成的图片做个对比:

    +

    +

    再对比一下没有参考图生成的图片Utah Teapot --ar 16:9 --seed 1

    +

    +

    显然,有参考图生成的图片在风格和形状上都更接近所提供的图,而没有参考图所生成的图片差异较大。

    +

    此外,你还可以通过参数--iw控制参考图的权重,默认是0.25。下面再分别给出权重为0.5和1时所生成的图:

    +

    +

    可以看到,AI在很努力地模仿参考图的颜色、形状,但仍然颇有难度。一般来说,用默认的数值就可以了,如果你想要参考图的权重更大些,设置为0.5也足够了。

    +

    实战操作

    +

    我们把上面说的综合起来使用给几个例子。

    +

    吉卜力风格的风景

    +

    第一个例子,我想生成一只小船在水村中航行的图,村落有着丰富的细节,吉卜力的风格,同时增加一些晨光。我可以用这个Prompt:A boat ride through a flooded seaside village, beautiful elaborate architecture, painted by Miyazaki, Nausicaa Ghibli, morning lighting, high saturation, spectacular details, epic composition, wide angle, low angle --ar 9:32 --q 1.5,经过几轮比较随意的迭代,我找到下面几张还不错的图:

    +

    +

    黑暗系风格的怪物

    +

    在第二个例子中,我想设计一个黑暗系风格的怪物。女神似乎在一般的作品中是一个正面的形象,那如果是一个腹黑女神呢?从这个出发,我试着尝试让AI画出一个黑暗系的腹黑女神,有白色裙子、邪恶笑容、黑色翅膀和金色花饰,绘画风格偏现实主义,艺术家选定为Dorothea +Tanning。

    +

    最后我把Prompt设定为a full body portrait of a wicked goddness, beautiful white dress, evil smile, red eyes, black wings, shining gold flowers on her hair, concept art, photo realistic, painted by Dorothea Tanning, back lighting, dramatic lighting, greyscale, intricate details, bold brushstrokes, mystical --ar 2:3,给出了下面的几张图(最后两张图使用了--testp):

    +

    +

    上面的图比较明显的不足是人物脸部,尤其是眼睛都没有得到很好的处理,这是当前MJ画全身照的缺点。当我们画半身照人物特写的时候一般没有这个问题。

    +

    蒸汽朋克风格的建筑

    +

    第三个例子,我们想画一个蒸汽朋克风格的建筑,细节越多越好。坐落在水边,有丰富的光影,整体基调呈现暖色。

    +

    因此,我们选用Prompta beautiful magnificent steampuck building by the seaside, view from the sea, rigorous architecture, ultra realistic, epic composition, wide angle, close up, morning lighting, volumetric lighting, warm colors, intricate details, 8K, hd, unreal engine, enchanting --ar 9:32 --test --creative,生成了下面几幅图:

    +

    +

    加入一点艺术家得到下面的图(图一二painted by Earl Norem, Edwin Lord Weeks,图三painted by Elizabeth Shippen Green,图四Ford Madox Brown,图五Farel Dalrymple,图六François Schuiten,图七Franz Marc,图八Georges Rouault):

    +

    +

    在实验的过程中发现:不要将--testp用于风景图,否则会有奇怪的东西;相反,在人物图上用--testp效果很好。

    +

    使用建议

    +

    基于上面的使用方法和我自己的实验,初步建议大家在使用的时候遵循下述规范:

      -
    • 玩家既是化身,又不是化身,这是游戏体验的核心。
    • -
    • 文化研究强调能动性(agency)这一概念,这是读者主动阅读时所具备的积极素质,因此我们可以认为对化身行动的操纵程度可以等同于更为广泛意义上的掌握文化权力的程度。 +
    • 重视参数--ar!很有时候宽高比会严重影响生成的图片,即使输入的其他内容完全一致。比如你想画个人物肖像,如果你的宽度太小无法容纳一张脸,那么AI就完全不会生成正确的肖像画;而如果宽度太大,则可能会出现多个人或者其他不必要的元素。一般来说,宽高比和画面内容的关系如下:
        -
      • 我们是自主有力的社会行为者:可以选择为玩家可以参与文本的程度达到前所未有的地步而欢呼。
      • -
      • 我们仅仅是他者的再现:玩家只能接受并操纵由公司设定的游戏文本决定提供给他们的角色。
      • +
      • 人物特写/Headshot:使用--ar 1:1或者--ar 3:4,并搭配headshot, +closeup, portrait等关键词
      • +
      • 人物半身照/全身照/角色设计:使用--ar 3:4--ar 9:16,搭配full body, +head and shoulder shot, over the shoulder, +medium shot等关键词
      • +
      • 风景图/远景:使用--ar 16:9,搭配landscape, +establishing shot, epic composition等关键词 +宽高比的选择完全取决于你想要生成怎样的内容,如果你想生成一个竖版的风景图,也完全可以使用--ar 9:16,总的原则就是!!#e06666 +画面内容与宽高比保持一致!!
    • -
    • 随着游戏在不同时刻在给予性结构或需求性结构上的转变,玩家的参与方式也将发生变化。战斗可能是最为偏向需求性结构的情况,此时游戏系统似乎称为游戏的全部。
    • +
    • 重视艺术风格!一个合适的艺术风格可以给你的画面带来极大的改变。当你需要偏现实的风格时,可以尝试realistic, +photo realistic, +ultra realistic等关键词,然后去找合适的现实主义风格的艺术家。当你需要特定的风格时,请精准描述艺术风格,比如浮世绘ukiyo-e,油画oil painting,流行艺术pop art,赛博朋克cyber punk,封面画cover art, +吉卜力Ghibli等等,这需要你对现有的艺术风格有比较丰富的了解!很多时候并不是你画不出来,而是你找不到对应的风格。你可以使用同一风格的多个艺术家作为关键词让画面更加倾向该风格。
    • +
    • 重视参数--test--testp!有时候仅用普通的2*2图片不能得到比较好的结果,尤其是Prompt较长的时候。此时,可以多用一下参数--test--testp,也许会带来意想不到的结果。注意,--testp不要用于风景图。
    • +
    • 重视参考图!尽管本教程没有过多阐述参考图的效果,但是当你手头有很多参考图时,不妨直接使用它们。记得调整参考图的权重--iw
    • +
    • 重视”魔法“关键词!有一些比较通用的关键词,比如intricate details, +unreal engine 5, enchanting, +ornate, after effect, +well composed, elaborate, +Sony Alpha等等,可能会提升画面的细节效果,不妨多试试它们。
    • +
    • 多尝试,出一张效果好的图需要运气,也需要认真地调试。
    -

    多模态的给予与需求

    -
      -
    • 文本产生的需求由文本当中的不同模态通过相互组合以不同方式实现。 -
        -
      • 如音乐节奏的变化暗示着游戏文本变化。
      • -
      • 如图像的变换预示着危险的迫近。
      • -
      • 此时玩家具有双重身份:一方面,玩家与化身融为一体,二者皆为行动者;另一方面,玩家又像是一个操纵木偶的人,拉着化身的线,甚至是某种类型的作者,作为建立在规则之上的因果结构的一部分,以一种受限语言进行书写。
      • -
    • -
    • 镜头元素同样有助于文本互动。 +

      最后奉上几张AI绘制的浮世绘风格的图片,希望大家使用愉快 ;p

      +

      +

      参考资料

      +

      MJ官方文档
      +关键词参考工具
      +更完整的关键词参考
      +艺术家参考
      +风格参考
      +其他人的作品参考1
      +其他人的作品参考2

      +

      NovelAI不完全使用指南

      +

      太长不看版:使用规范

      +
        +
      1. 首先按照公式 Prompt = 起手叠BUFF + 构图说明 + +画面内容 + 画面风格 + 光影设置 + 颜色设置 + 其他意象 +去写Prompt,具体来说:

          -
        • 置于较低处,好像和角色并肩作战,去除了需求性结构所产生的木偶操纵般的感觉。如果我们被赋予了操纵能力,同时视角又较高,则操纵的感觉会相当强烈。在需求性结构处于最紧张的时刻,将镜头拉低和拉近能够使玩家不失时机地与角色接近。【战神4】
        • -
        • 镜头通常置于角色上方固定的位置,以提供操纵感,让玩家像对待、培养宠物一样对待化身,提供了给予性结构,并提供一定的稳定性。
        • +
        • 起手叠BUFF:把下面的内容放到你要写的Prompt最前面,起手BUFF还是比较重要的: +{masterpiece}, {best quality}, {ultra-detailed}, illustration, beautiful, 8K, small breasts +最后的small breasts可以换成madium breasts或者其他(你懂的)。
        • +
        • 构图说明:人物在画面中的位置、大小、角度等等,常用的有portrait(特写)、medium shot(半身照)、full bodyupper body(全身照)、dutch angle(倾斜镜头)、wide angle(广角镜头)、side view(从侧面看)、back view(从后面看)等等。
        • +
        • 画面内容:画面里需要包含的各种内容,可以从人物本身和背景两个角度分别描述。人物内容包括头发、脸部、眼睛、肩膀、耳朵、配饰、手、服装、手套、鞋子等等,但首先需要指定包含几个人,比如1 girl, +solo;背景就根据自己的需求增加内容即可,比如dragon background, +forest background, beautiful milkyway, +burning bettlefield等等。此外,你还可以使用关键词reference sheet生成三视图、设计图。
        • +
        • 画面风格:画面的美术风格,常用的包括realistic, +outline, sketch, flat color, +watercolor (medium), grey scale, +ukiyo-e, cover art, poster, +comic, art nouveau, cyberpunk, +sci-fi, wildstyle, 等等。
        • +
        • 光影设置:和MJ一样,设置画面的光影,主要包括:背光backlight, +电影打光cinematic lighting, 圣光holy light, +日光sunlight, 月光moonlight, +波光粼粼glistening light of waves, +金色光golden light等等,你也可以根据需求创造属于你的光影。
        • +
        • 颜色设置:使用颜色关键词让画面整体更偏向某种颜色。
        • +
        • 其他意象:你可以加入任意多的其他意象词为画面添加细节和内容,比如:阳光sunlight, +河流river, 水晶crystal, +棱镜prism, 冰ice, 浮动floating, +照射shine, 影子shadow, +装饰ornament/decoration/frills, 火焰flames, +火花sparks, 光晕flares, +核爆nuclear explosion, +飞溅的血splashing blood, +飞舞的花瓣flying petals, 等等。
      2. -
      3. 对游戏世界的探索感得自一种更弱的需求形式——诱导而非命令。【只能在有强烈需求的时候才能给予玩家强烈的暗示,或命令;而在一般情况下,需求更多给予微弱的引导。这一方面战神做得很好。】
      4. -
      5. 【游戏需要不断地、交叉地提供需求性结构和给予性结构。】
      6. -
    -

    文本再创作:网络上的粉丝文化

    -
      -
    • 在游戏的需求性结构(搅动因果链条,推动你跨越失事列车或迷宫实验室的解密关卡,使你陷入紧张情绪之中,让你迷路和让你在面临众多敌人的时候产生焦虑感)和给予性结构(安排前后语境、风景、背景故事、动机和心理,将读者-受众拉入文本的移情网络和想象性延展空间中,和传统叙事中的运作方式一样)之间存在一种辩证关系,正是二者的结合提供了快感,而在以游戏为核心的粉丝创作中,二者将以相当不同的方式结合在一起。
    • -
    • 粉丝是如何使用游戏系统的?游戏攻略、创意性写作、漫画。
    • -
    -

    游戏攻略作者

    -
      -
    • 这是一种探索游戏系统本身的快感。
    • -
    • 他们用祈使句(第二人称)控制彼此的游戏行为——这是游戏攻略的特征。
    • -
    -

    虚构文学写作者

    -
      -
    • 过场动画属于给予性结构——它们只作叙事性陈述,并无互动性。过场动画可能表现出作为核心的游戏和外层叙事之间的分离。
    • -
    -

    诗人

    -
      -
    • 直接阐释了游戏主角的情感,这是游戏文本无法做到的。【现代游戏在有面捕和动捕之后是可以实现的。】
    • -
    -

    “三无”漫画家

    -

    结论

    -
      -
    • 在围绕《FF7》的粉丝创作当中,再现系统和游戏系统是彼此分离的。
    • -
    • 以再现系统复制对玩家与化身的第一人称认同是可能的,但更为常见的还是借助游戏再现系统所提供的可能性,生产出第三人称的视角、叙事和形象创造,并为了自己获得快乐而对其进行挑选、放大或转换。【采用第一人称就限制了创作空间,而第三人称视角提供了无限大的创作空间,不仅仅限于游戏文本所提供的现有信息。】
    • -
    -

    动机与网络游戏

    -

    再现动机:欢迎来到鲁比卡世界

    -
      -
    • 《混乱在线》以科幻小说为基础进行设定,在游戏网站上,鲁比卡世界的故事片段以各种形式呈现。
    • -
    • 我们借以创建化身的个人标准,远比我们最初所能意识到的更能暴露自身经历及偏好。
    • -
    -

    娱乐动机:游戏、目标与策略

    -
      -
    • 娱乐动机相关的要素:战略、目标、即时事件、机遇、规则、技能、探险、升级、数值。
    • -
    • 娱乐动机更强调玩家所操纵的游戏中的那个化身作为游戏的一个元素存在,是一个具有战略价值的符号单元,而非一个角色。
    • -
    -

    公共动机:共享鲁比卡世界

    -
      -
    • 公共动机包括一个游戏和其他游戏之间的关系、玩家由游戏之外带入游戏的期待,以及游戏本身的共享属性。
    • -
    -

    结论

    -
      -
    • 游戏动机的三种类型:再现动机(戏剧性的、表演性的、修饰性的、图像化的、叙事的)、娱乐动机(指向游戏层面的:数值、升级等)和公共动机(共有的期待、广泛的网络文化、游戏所分享的外部世界)。
    • -
    -

    社交性的游戏与学习

    -

    探究游戏的社交性

    -
      -
    • 游戏较之更为传统的、只能独自体验的媒介,比如书籍,可能是更适合儿童的娱乐形式。
    • -
    • 游戏文化可以被认为视为这样的一个竞技场:它创建了等级制度,并且欺凌群体当中的弱者。
    • -
    -

    观察玩家

    -
      -
    • 游戏过程当中与其他玩家之间的互动似乎已经成为游戏乐趣的一个重要部分。
    • -
    -

    游戏

    -
      -
    • 《凯恩的遗产:噬魂者》。
    • -
    -

    玩游戏

    -
      -
    • 玩家与游戏机之间的联系可以在同伴的指导和建议下构成,这产生了一种有效的“中介性合作”。
    • -
    • 无论小组中的不同成员扮演什么角色,我们从男孩们的游戏失败的同步反应中,可以进一步证实合作游戏的集体性本质是显而易见的。
    • -
    -

    叙事与游戏

    -
      -
    • 如何处理游戏角色的死亡对动作冒险游戏而言十分重要。《噬魂者》的处理方式是:死亡不会导致重新开始,而是进入阴间,这使得玩家在相似环境下继续游戏,且包括了许多之前不存在的道路。
    • -
    • “管控调整”语言明显见于游戏和玩家之间以及玩家和玩家之间所交换的各种问题和命令中,“高手”完全关注于游戏系统以及对操作的指导。
    • -
    • 电脑游戏的试听魅力明显降低了在游戏过程中当面讨论的需要,如果故事已经展现在你眼前,描述故事甚至事无巨细地加以讨论就显得多余。
    • -
    • “玩后叙事”可以更多地展示出游戏地叙事魅力。
    • -
    -

    结论

    +
  • 把下面的内容写到Undesired +Content中,然后再加入你想屏蔽的其他关键词: +{{{ugly}}},{{{duplicate}}},{{morbid}},{{mutilated}},{{{tranny}}},{breast},mutated hands,{{{poorly drawn hands}}},{{bad anatomy}},{{{bad proportions}}},extra limbs,cloned face,{{{disfigured}}},{{{more than 2 nipples}}},{{{{missing arms}}}},{{{extra legs}}},{{{{{fused fingers}}}}},{{{{{too many fingers}}}}},{{{unclear eyes}}},{{{fused hands}}},{{{fused leg}}},{{{bad feet}}},nsfw,lowers,bad anatomy,bad hands,text,error,missing fingers,extra digit,fewer digits,cropped,worst quality,low quality,normal quality,jpeg artifacts,signature,watermark,username,blurry,bad

  • +
  • Steps默认28,Scale默认为7,当然你可以根据实际需求调整这两个值。Samping使用默认的k_euler_ancestral即可;

  • +
  • 对你比较满意的图使用VariationEnhance,反复迭代,直到满意为止。

  • + +

    概述

    +

    NovelAI是基于Stable +Diffusion模型改进的AI绘画工具,它擅长绘制二次元人物图,虽然也可以把它当作综合性的绘画工具,但是生成的图片偏写实,质量不如Midjourney。

    +

    写NovelAI +Prompt的基本准则是:用关键词(或者称为Tag)描述,而不要用短语甚至句子。关键词包括画面内容(人物头发、眼睛、表情、服饰、姿势、手部、胸部、肩部,等等)、画面风格、构图设置、光影设置、颜色设置、意象词和叠BUFF词等等。

    +

    总的来说,写NovelAI +Prompt相比MJ更容易些,但要实现精准调教仍然难度很大。下面会详细介绍。

    +

    NovelAI官方文档
    +关键词参考

    +

    注册账号(官方)

    +
      +
    1. 登陆官网,注册并登陆账号。

    2. +
    3. 之后在打开的页面上点击“Generate Images”,或者直接通过网页进入:

    4. +
    5. 最后输入Prompt并调整右侧参数开始使用:

    6. +
    +

    本地版本

    +

    To do

    +

    使用教学

    +

    NovelAI的Prompt跟MJ差不多,主要遵循下述公式: Prompt = +起手叠BUFF + 构图说明 + 画面内容 + 画面风格 + 光影设置 + 颜色设置 + +其他意象

    +

    在介绍每个部分之前,需要先讲解下NovelAI各个参数的作用。

    +

    NovelAI的参数

    +

    +

    从上到下,从左到右:

      -
    • 作为一种指导性活动,合作性游戏比明确地尝试指导更为有效。
    • -
    • 电脑游戏嵌入了“社交外壳”,单人游戏可以转变为包含有效指导与沟通的社交活动。游戏都是那种可以形成社群和身份认同感的学习形式的绝佳例证。
    • -
    • 互动不仅指玩家和游戏之间的关系,也应包括很多玩游戏这一活动所固有的社会语境和社交关系。
    • +
    • Prompt:在这个地方输入你的Prompt,使用大括号{}增加一个关键词的权重,使用中括号[]去减少关键词的权重,支持嵌套,比如{{magical}}就表示生成图像的时候会特别关注magical的内容,而[[[green]]]则表示生成时尽量避免生成绿色的内容;
    • +
    • 分辨率:在这里设置你图像的分辨率,可以使用预设,也可以手动输入,这个参数非常重要,同MJ,要和你生成的内容相匹配
    • +
    • Number of images:生成图像的数量;
    • +
    • Undesired +Content:输入不想要AI生成的内容的关键词;
    • +
    • Add Quality Tags:默认勾上就行;
    • +
    • Steps:生成一张图需要的步数,步数越大,生成的时间越长,而且效果也不一定好,一般使用默认值28就好了,除非你已经找到一个很好的Prompt想要增加更多的细节;
    • +
    • Scale:控制所生成图像匹配你输入Prompt的程度,值越小,画面越风格化和柔和,值越大,画面越细节和尖锐,但设置过大可能导致效果变差,一般来说使用小于10的值;
    • +
    • Sampling:生成时的采样方法,一般而言使用默认的即可。
    -

    游戏内外的能动性

    +

    +

    在生成图像后,会多出来一排选项,其中比较重要的是后面两个:

      -
    • “互动性”一次常用以解释电脑游戏的魅力。三种类型的交流模式:声明式交流、反应式交流和互动式交流。从这个角度来看动作冒险游戏,它更像式“反应式交流”而非“互动式交流”。
    • +
    • Variations:生成当前图片的变体,在细节上会有不同,但大体都是一样的;
    • +
    • Enhance:对当前图像进行增强,会较显著地增加细节。但注意不要把Noise调太高。
    -

    进入奇异世界

    +

    起手叠BUFF

    +

    NovelAI要把关键信息放在Prompt的前面,因此我们一开始就要叠BUFF,可以先无脑加入下面的BUFF,然后再根据你的需求自行添加: +{masterpiece}, {best quality}, {ultra-detailed}, illustration, beautiful, 8K, small breasts

    +

    注意上面的最后一个BUFFsmall breasts限制了生成角色胸的大小,对于女性角色必须要有(否则全是涩图)!你如果不喜欢平胸,可以用medium breasts,或者你生成的不是女性,就把这个去掉即可。

    +

    构图说明

    +

    和MJ一样,可以用portrait表示特写,用medium shot/upper body表示上半身构图,用full body shot表示全身照。

    +

    除此之外,还可以用dutch angle表示倾斜镜头,用wide angle表示广角镜头,用low angle表示低角镜头,用depth of field增加景深,用side view表示从侧面看,等等。你可以根据自己想象中的内容选择合适的组合。

    +

    !!#3d85c6 这个网站有一些主要的关键词:https://aitag.top/ +。下面的所有内容都可以去参考这个网站,不再赘述。!!

    +

    比如下面我用了一些不同的构图关键词去生成a beautiful girl(关键词分别是portrait, +medium shot, full body shot, +full body shot, dutch angle, +portrait, dutch angle, depth of field, +portrait, side view, +full body shot, back view, +full body shot, from above):

    +

    +

    画面内容

    +

    你首先需要明确图片中包含几个角色,一般来说是一个,那么你只需要加入solo1 girl/1 boy即可。如果是两个,就是two girls,以此类推。

    +

    然后,你需要描述这个角色的各种细节,可以从下面角度考虑(不一定都要,看你需求):

      -
    • “奇异世界”的创造者和设计者表示,他想将生态困境注入作为整体的游戏,让玩家可以与之互动,并最终战胜困境。
    • +
    • 头发:disheveled hair, floating hair, +azure hair, long hair, +short hair, beautiful hair, +white hair, curly hair, bob hair, +polytails, updo, +twintailsside blunt bangs,等等
    • +
    • 脸部:tears, cold attitude, +smile, sad, annoyed, +delicate beautiful face, +detailed face,等等
    • +
    • 眼睛:Lavender eyes, crystal eyes, +bright eyes, beautiful detailed eyes, +half closed eyes, hollow eyes, +blank stare, rainbow eyes, +gradient eyes, sparking eyes,等等
    • +
    • 肩膀:bare shoulder, off shoulder
    • +
    • 耳朵:pointy ears
    • +
    • 手:outstretched arms, arms behind back, +hands on hips, hand on own face, +hugging own legs, hand in own hair, +holding flowers,等等
    • +
    • 配饰:gold accessories, white lightsaber, +tail, scarf, armor headdress, +ribbon, neck ribbon, hair ribbon, +halo, necklace, wings, +tassel, earrings, wizard hat, +headphone, +red swordfloral print, 等等
    • +
    • 服装:detailed mechanical armor, +detailed organdie dress, skyblue dress, +princess dress with delicate gold metal decorations, +witch dress, white thin detailed cloak, +summer long skirt, angel suit, +very long dress, +translucent fluttering dress with lacekimonotrench coat, +cheongsampettiskirt, +lolita gothicpleated skirt, 等等
    • +
    • 手套/袖子:detailed white gloves, +elbow gloves, sleeveless, +wide sleeves, large top sleeves, 等等
    • +
    • 鞋子:barefoot, thigh boots, +getauwabaki, 等等
    -

    对玩家能动性的研究

    +

    建议平时可以多看别人的关键词然后记录下来。

    +

    使用不同的组合并加入不同的权重可以产生你想要的效果,比如下面的例子:

    +

    +

    除了角色本身的细节之外,你还可以指定背景,比如没有背景no background, +以龙为背景dragon background/loong background,以森林为背景forest background,大火为背景fire background/burning background,如下(不同的背景需要不同的权重):

    +

    +

    特别说明:如果你想要生成人物设计图(即三视图),你可以用reference sheet,并同时修改分辨率

    +

    +

    画面风格

    +

    顾名思义,就是需要选择图片的美术风格,下面有一些供参考的风格及其关键词选择:

      -
    • 论坛的集体性质还表现在以下事实中:论坛成员对论坛环境负有管理职责,他们鼓励参与者以相互支持而非破坏性的方式参与讨论。
    • +
    • 写实:realistic, photorealistic
    • +
    • 素描:sketch, rough sketch, +pencil sketch
    • +
    • 描边:outline
    • +
    • 线稿:lineart
    • +
    • 像素:pixel art
    • +
    • 平涂:flat color
    • +
    • 平面着色:flat shading(注意和平涂不一样,下有例子)
    • +
    • 水彩:watercolor (medium), +watercolor pencil (medium)
    • +
    • 单色:monochrome, spot color, +greyscale
    • +
    • 浮世绘:ukiyo-e
    • +
    • 苏维埃海报:soviet poster
    • +
    • 封面:cover art
    • +
    • 漫画书:comic book
    • +
    • 动漫:comic
    • +
    • Q版:chibi
    • +
    • 复古艺术:retro artstyle
    • +
    • 新艺术派:art nouveau
    • +
    • 年代:1970s, 1980s, +1990s
    • +
    • 赛博朋克:cyberpunk
    • +
    • 狂野风:wildstyle
    • +
    • 科幻:sci-fi
    • +
    • 奇幻:fantasy
    • +
    • 传统日本风格:traditional Japanese art
    -

    个人能动性

    +

    建议把风格使用至少三个大括号{{{}}}甚至更多包裹起来进行强调,确保可以生成正确的风格图。

    +

    你可以选择同一个风格里的多个关键词,或者结合不同的风格。比如你可以融合像素风pixel art和奇幻风fantasy形成像素奇幻风。但最好不要融合超过两种风格,否则生成结果未知。

    +

    下面是一些例子(依次是{{{flat color}}}, +{{{flat shading}}}, +{{{soviet poster}}}, {{{flat color}}}, +{{{outline}}}, {{{sketch}}}, +{{{{{traditional media}}}}}, +{{{ukiyo-e}}}, {{{outline}}}, +art nouveau, +{{{pixel art}}}, {{fantasy}}, +{{{black and white}}}, {outline}, {flat shading}, {flat color}, {concept art}, {lines}, +{{{{wildstyle}}}}, {flat color}, +{{{{{{{{wildstyle}}}}}}}}, {cyberpunk}, {{{outline}}}, +{traditional japanese art}, {anime}, {fantasy}):

    +

    除了显式指定美术风格之外,你还可以指定艺术家和作品让画面偏向某种特定的风格。但是和MJ不同的是,NovelAI并不像MJ那样非常依赖艺术家,一般不加,或者最多只加一个艺术家或作品即可。比如下面我分别指定了ghibili, +Hayao Miyazaki, +breath of the wilddark soul

    +

    +

    可以看到,加入艺术家和作品并没有想象中的那样有效,所以推荐不加。

    +

    注:当然如果你非常熟悉某个艺术家,那加入艺术家也是可以的,但一般来说需要给艺术家比较强的权重模型才会生成比较相似的风格,而且也不是所有艺术家都支持的,还是建议多做尝试。这个表是已记录的一些艺术家,可以根据风格先在谷歌上搜索,然后自行尝试。

    +

    光影设置

    +

    这里的光影设置和MJ是一样的,比如下面的光影: +背光backlight, 电影打光cinematic lighting, +柔和光soft lighting, +体积光volumetric lighting, +点光(聚光灯)spotlight, 圣光holy light, +日光sunlight, 月光moonlight, +波光粼粼glistening light of waves, +金色光golden light

    +

    +

    颜色设置

    +

    颜色没太多好说的,如果你想要画面整体偏某种颜色,直接加入颜色关键词即可。 +但更好的方法是直接指定某个物体的颜色,比如red eyes, +cyan hair,以实现精准控制。有时候需要加大物体的权重,避免这个颜色控制了其他部件。

    +

    其他意象

    +

    其他意象词一般用来增加前景和背景的丰富度,以及人物身上的细节,比如下面的一些关键词: +羽毛feather, 自然nature, +叶子leaves, 阳光sunlight, +河流river, 水晶crystal, +棱镜prism, 冰ice, 齿轮gear, +流动flowing, 浮动floating, +照射shine, 影子shadow, 时钟clock, +装饰ornament/decoration/frills, 火焰flames, +火花sparks, 光晕flares, +核爆nuclear explosion, 闪电lightning, +飞溅的血splashing blood, +飞舞的花瓣flying petals, 微风breeze, +风wind, 雨rain, 云clouds, +烟smoke, 雾mist, 纱yarn, +沙sand, 星尘stardust, +银河milkyway, 旋转swirling, +头骨skull, 骨骼skeleton, +几何geometric, 立方体cubic, +多边形polygon

    +

    总之你可以添加任何你想要在图片中出现的意象词,但要注意和整体画面内容的搭配。你可以通过调整词的权重控制意象出现的频率。

    +

    还是建议找一些相关的参考图和别人给的关键词,多做尝试。

    +

    负面关键词

    +

    负面关键词是你不想让它出现在图片中的内容,填入Undesired +Content中即可。虽然是根据你不想要的内容去选择负面关键词,但是也要一些通用的负面关键词。下面是默认添加的关键词,其他关键词根据需求自行添加: +{{{ugly}}},{{{duplicate}}},{{morbid}},{{mutilated}},{{{tranny}}},{breast},mutated hands,{{{poorly drawn hands}}},{{bad anatomy}},{{{bad proportions}}},extra limbs,cloned face,{{{disfigured}}},{{{more than 2 nipples}}},{{{{missing arms}}}},{{{extra legs}}},{{{{{fused fingers}}}}},{{{{{too many fingers}}}}},{{{unclear eyes}}},{{{fused hands}}},{{{fused leg}}},{{{bad feet}}},nsfw,lowers,bad anatomy,bad hands,text,error,missing fingers,extra digit,fewer digits,cropped,worst quality,low quality,normal quality,jpeg artifacts,signature,watermark,username,blurry,bad

    +

    实战操作

    +

    下面还是给几个实战操作的例子。

    +

    和服风的设计稿

    +

    第一个例子,我们想要一个三视图设计稿,主角是一个穿着和服的传统日本女孩,盘发、头上有红色的花、化妆、精致的手镯、漂亮的印花。因为是设计图,所以要调整下分辨率,用默认的Landscape就好了。因此,我用的Prompt是:{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, small breasts, full body, solo, a japanese girl, {{{{{reference sheet}}}}}, flat color, concept art, brown hair, red flower in hair, {updo}, smile, beautiful face, beautiful makeup, delicate bracelet, {beautiful kimono with intricate floral print}

    +

    下面是生成的一些图(尝试了不同的Steps和Scale,Steps大则细节更丰富,Scale越小则多样性越强,但建议Steps<=40, +Scale>=6。最后一张图给和服加了blue):

    +

    可以看到,人物的手和脚是重灾区,但其他地方还是可以的。另外,活用Enhance能够极大地修复手的问题,关键在于Strength的参数不要太大(0.3左右),Noise设置为0或者非常小。

    +

    机甲少女全身照

    +

    第二个例子,我想要画一个机甲少女的全身照,有着冷酷的表情、红色的眼睛、脸上有纹身、拿着一把红色的刀,我并不太想指定其他过多的元素,但是想要图片的背景是弥漫着硝烟的战场,空中也飞舞着火星。因此,我使用的Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, small breasts, full body, depth of field, solo, a mechanical girl, detailed mechanical armor, detailed mechanical body} {holding a red sword}, disheveled hair, short hair, cold stares, half-closed eyes, {{dark red eyes}}, gradient eyes, ruined battlefield background, {detailed background}, burning buildings, splashing sparks, flames, holy light

    +

    下面是生成的图(使用了不同的Steps和Scale,最后四幅图修改了机甲的颜色): +

    +

    狂野赛博艺术图

    +

    作为我们的第三个例子,我们将探索wildstyle这个风格与其他关键词的组合会得到怎样的效果。wildstyle意味着丰富的色彩,尤其是大面积深色的应用。我们将融入赛博朋克的元素,并搭配不同的关键词看AI会给出我们怎样的结果。我使用的基础Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, {{{{wildstyle}}}}, beautiful, small breasts, solo, {{a girl}}, cyberpunk

    +

    下面是生成的图(每张图都调整了wildstyle的权重,附加Prompt依次是, +{flat color}, +{{flat color}}, {colorful}, +{{flat color}}, {{outline}}, +sci-fi, +{flat color}, beautiful kimono with cherry floral print, +{flat color}, watercolor (medium), +{{{pixel art}}}):

    +

    结果发现,wildstyleflat color, +outline等关键词搭配效果很好。

    +

    华丽高贵女神范

    +

    第四个例子,我想要得到一个华丽高贵的女神,被白色的花簇拥着,穿着华丽的白色礼服,点缀金色丝边,戴着项链、手镯、花环,蓝色的发光的眼睛。分别尝试不同的构图,即特写、半身、全身,和不同的风格,对背景不做要求。采用的Prompt是{{masterpiece}}, {{best quality}}, {{ultra-detailed}}, illustration, beautiful, 8K, very detailed, small breasts, [composition], [style], solo, a royal goddess, disheveled hair, detailed blue eyes, gradient eyes, glowing eyes, vibrant colorful garland, gorgeous princess dress with delicate gold metal decorations, {{white flower decorations}}, {{surrounded by flowers}}, exquisite bracelet, exquisite necklace, bare foot, {{flowing flowers}}, {{liquid}}。其中,[composition]填写构图,[style]填写风格。

    +

    下面是生成的一些图片(前两张是portrait, +第三四张是head and shoulder shot, +最后四张是full body;风格第一张是cartoon, anime, +第二张realistic, 第三张cartoon, anime, +第四张fantasy, flat shading, +第五张flat color, geometric, cubic, +第六张cartoon, anime, +第七张flat color, flowing,第八张dark magic, Cthulhu): +

    +

    使用建议

      -
    • 玩家可以以多种方式参与游戏,尽管游戏具有强烈的叙事目标,但它仍可以被从视觉、情感和主题等多个层次加以解读。
    • +
    • 重视分辨率!道理和MJ一样,即图片的分辨率要和想要生成的内容匹配,不再赘述,详细请看MJ页面。
    • +
    • 尝试权重!NovelAI对关键词加权是非常重要的一个操作,有时候你写的关键词没有生效极有可能就是关键词的权重不够导致的,这时候多尝试嵌套几层大括号{{{}}}。一般来说,对
    • +
    • 叠通用BUFF!上面给出了最基础的通用BUFF,但是对某些类型的图来说,还有一些额外的BUFF可以叠,建议多看看对不同的美术风格、画面内容,别人怎么叠BUFF的,总结一套属于自己的BUFF表。之后有时间我也会帮大家总结。
    • +
    • 加入风格!加入风格(包括与风格有关的关键词)会有助于生成你想要的内容,但注意与MJ不同,NovelAI对艺术家的支持并不好,所以尽量不要用艺术家。加入flat shadingflat color偶尔会有奇效,其他的一些意向词比如flowing, +geometric也能创造很好的风格。
    • +
    • 多尝试参数!NovelAI的参数虽然没有MJ多,但是调试更加困难。一般来说用Steps=28, +Scale=7的默认参数能够得到还不错的效果,如果你发现怎么改关键词都不生效的话,果断尝试修改参数吧(当然也有可能是NovelAI的训练集中没有你输入的内容,弃疗吧)!
    -

    代理能动性

    +

    参考资料

    +

    NovelAI官方文档
    +NovelAI图像生成注意事项
    +关键词参考
    +元素法典——Novel AI +元素魔法全收录(第一卷)

    +]]>
    + + 随笔 + + + 随笔 + 计算机 + 游戏 + 工具 + 绘画 + 深度学习 + +
    + + The Jittering Issue with Damping in Cinemachine and How to Tackle it + /2023/07/08/18/22/ + If you are familiar with Cinemachine. you probably know there is a +knotty problem with Cinemachine' damping if you are using +Framing Transposer or some other components to track a +follow point. That is, the camera jitters with damping enabled under +unstable frame rate. The more unstable frame rate is, the more heavily +camera will jitter. This post will discuss this phenomenon and proposes +a workaround to solve this issue.

    + +

    Camera jitters with +damping in Cinemachine

    +

    Unity's Cinemachine has a notoriously severe problem that may cause +the follow object to seemingly jitter when you are using the +Framing Transposer component with damping enabled.

    +

    To show this, I did a simple experiment. I created a new blank scene +and spawned a new attached with the following script:

    +
    public class CubeMove : MonoBehaviour
    {
    public float speed;
    public int fps;

    public float CurrentSpeed
    {
    get { return currentSpeed; }
    }

    private float elapsedTime = 0.0f;
    private float currentSpeed;

    void Start()
    {
    if (fps != 0)
    {
    Application.targetFrameRate = fps;
    }

    currentSpeed = speed;
    }

    void Update()
    {
    elapsedTime += Time.deltaTime;

    if (elapsedTime >= 5.0f)
    {
    if (currentSpeed > 0.0f)
    {
    currentSpeed = 0.0f;
    }
    else
    {
    currentSpeed = speed;
    }

    elapsedTime = 0.0f;
    }

    transform.position += new Vector3(1, 0, 0) * currentSpeed * Time.deltaTime;

    }
    }
    +

    This script moves the cube for 5 seconds and then keeps it steady for +another 5 seconds and continues moving. The move speed as +well as the fps (frames per second) can be set for test +under different conditions.

    +

    A new virtual camera is then created with a +Framing Transposer component following this cube. A default +damping of 0.2 is used.

    +

    Here is result with speed is 100 and fps is +0 (when set to 0, the real fps is determined by Unity, may but +unstable).

    +

    +

    The jitters are very clear. You can also notice that the frame rate +(presented in the Statistics panel) is very unstable, and we will know +soon it is the unstable fps that results in camera jitters.

    +

    Cinemachine proposes a workaround to alleviate this problem, that is, +to use the revised version of damping where they sub-divide each frame +and simulates damping in the consecutive series of sub-frames. To enable +this functionality, go to Edit -> Project Settings -> Player -> +Script Compilation and add the +CINEMACHINE_EXPERIMENTAL_DAMPING marco to it.

    +

    +

    OKay, now we have enabled the new damping algorithm and let's see how +it will mitigate the jittering issue. Here is result with the same +setting we used in our previous experiment, i.e., speed is +100 and fps is 0.

    +

    +

    It is astonishing to see the jittering issue becomes even more +severe. I conjecture that the variance of fps will significantly amplify +camera jitters when this feature is enabled. In other words, the +experimental damping algorithm responds to the variance of fps in a +NON-linear way: when the variance is small, the experiment damping will +reduce the gaps of camera location between contiguous frames; but when +the variance is large, it will enlarge the gaps, leading to unacceptable +jittering. (Note: I did not validate this conjecture. If you are +interested, just review the code and test it yourself.)

    +

    What about the expected result if fps is stable? Let's take more +experiments!

    +

    Here is result with speed is 100 and fps is +120 (very high fps, which is usually prohibitive in shipped games).

    +

    +

    Very steady camera! What about setting fps to 60? Here +is the result.

    +

    +

    An fps of 60 performs equally well with 120, which is anticipated as +fps is stable. Okay, let's try a final experiment where fps is set at an +extreme value of 20.

    +

    +

    Even a low fps of 20 makes our camera stable, only if fps itself is +stable.

    +

    Now we can conclude that it is the instability of fps that induces +camera jitters, regardless of the exact value of fps. But, why?

    +

    Why camera jitters

    +

    Before answering this question, let us first take a look at the +source of damping implemented in Cinemachine.

    +
    public static float Damp(float initial, float dampTime, float deltaTime)
    {
    if (dampTime < Epsilon || Mathf.Abs(initial) < Epsilon)
    return initial;
    if (deltaTime < Epsilon)
    return 0;

    public const float kNegligibleResidual = 0.01f;
    const float kLogNegligibleResidual = -4.605170186f; // == math.Log(kNegligibleResidual=0.01f);
    float k = -kLogNegligibleResidual / dampTime; //DecayConstant(dampTime, kNegligibleResidual);

    #if CINEMACHINE_EXPERIMENTAL_DAMPING
    // Try to reduce damage caused by frametime variability
    float step = Time.fixedDeltaTime;
    if (deltaTime != step)
    step /= 5;
    int numSteps = Mathf.FloorToInt(deltaTime / step);
    float vel = initial * step / deltaTime;
    float decayConstant = Mathf.Exp(-k * step);
    float r = 0;
    for (int i = 0; i < numSteps; ++i)
    r = (r + vel) * decayConstant;
    float d = deltaTime - (step * numSteps);
    if (d > Epsilon)
    r = Mathf.Lerp(r, (r + vel) * decayConstant, d / step);
    return initial - r;
    #else
    return initial * (1 - Mathf.Exp(-k * deltaTime));
    #endif
    }
    +

    Translating into mathematics, we have:

    +

    +

    where is the damp time +parameter and the elapsed +time in this frame. This equation decays the input , the distance for the +camera to go to the desired position, by an exponential factor . If , the residual will be , meaning that at +this frame, the camera will traverse 99% of the desired distance to go, +only remaining 1% amount for future frames.

    +

    OK, let's assume we've placed a cube in the origin and it moves along +the x-axis at a fixed speed, say, +m/s. A camera is placed to track the cube with damping where damp time +. Let's further denote the +delta time for each frame by , where is the -th +frame.

    +

    Having all variables fully prepared, we can then simulate the object +movement and camera track process.

    +

    In the beginning of 0-th frame, the camera and the cube are both at +the origin, i.e., (0, 0, 0). As the cube only moves along x-axis, we can +emit the y and z dimensions and use a one-dimensional coordiante to +represent cube and camera positions.

    +

    At the 1-th frame, the cube moves to , the +distance the camera traverses is , and the residual is . We set for simplicity.

    +

    At the 2-th frame, the cube moves to , the distance the camera traverses is +, and the residual is .

    +

    At the k-th frame, we have , +, and +.

    +

    Without loss of generality, we can set . The following sections will use this +settings unless otherwise stated.

    +

    For different combinations of , may have different results. Let's dive into and see how it influences +the results.

    +

    Case 1: Stable FPS, +all are equal

    +

    When all are +equal, say , our equations +reduce to:

    +

    +

    apparently has a +limitation of when +since . This +explains why a camera with damping always has a maximum distance to its +following target. There maximum distance, also the supremum, is exactly +. When is larger, will be larger, implying the +maximum distance between the camera and its following target will be +larger.

    +

    What if is +mutable? In this case, we can assume there exists an upper bound such that all satisty . Then we are +able to derive the same conclusion.

    +

    Another question is, why camera does not jitter when FPS is stable? +We turn to examine the sign of :

    +

    +

    Therefore, when FPS is stable, is always larger than , and jitter will never +happen.

    +

    Case 2: Unstable FPS, vary

    +

    When FPS is unstable, where may mutate, how will the camera move in response to its +following target? We can still examine the sign of , but in another +way:

    +

    +

    This equation uncovers why camera jitters happen with unstable FPS. +The residual at the k-th frame is essentially an interpolation +between the following target's current position increment and the last frame's +negative residual , where +the interpolation strength is the decaying factor . +As both and are fixed, a change in will incline the resulting +residual to different ends, +either or .

    +

    In our simplified case in which the target moves at a fixed speed in +the direction of x-axis, will always be positive (though its magnitude can vary) and + will always be negative. +A mutating thus has +a chance to alter the sign of , which further brings +about camera jitters.

    +

    So when will camera jitter? From the above equation, we know that +camera will jitter when the sign of consistently changes +over time, i.e., the value of oscillates around zero. +Let's make it equal to zero and see what we can find then.

    +

    +

    This equation tells us when is near , camera will have a large chance to jitter. This +motivates us to improve damping by filtering out the occasions where + is very close to +.

    +

    What about going deeper? We can treat as variable, and all other +as constants. This abstraction gives us a function of :

    +

    +

    Taking the derivative of , +we know that is monotonically +decreasing when and monotonically increasing when , and . Hence, to make the sign of mutable, must be positive and the +minimum of must be +negative.

    +

    The minimum of can be +easily computed:

    +

    +

    The last inequality holds because .

    +

    +

    This reveals the fact that: when , a variant is likely to cause to change its sign, +thus resulting in camera jitters. Suppose is large enough, so then +the k-th residual gets +smaller than while is positive. A smaller pushes to become smaller for the next frame, +which further pushes the root of the function to become larger. In this +case, even with the same delta time, will have a larger chance wo +fall in the negative area, i.e., is more likely to be less than the root.

    +

    +

    Solutions

    +

    Solution 1: imposing an +invalid range

    +

    Based on what we've discussed so far, we can immediately come up with +a simple solution: enforce to be if they are very close. That is to say, we use a +small value , if , we just set to .

    +

    Note that can be zero or negative. If this is the case, we keep the +original without +doing anything. Besides, you should be aware that here is not the time this +frame actually takes, instead, it is just the duration used to calculate +damping.

    +

    Let us explain it more quantitatively. Suppose , +where . +Then according to our algorithm. We then +plug into the original +expression of :

    +

    +

    This demonstrates that now the camera lags behind its following +target more than the previous frame since the residual is larger. After +substituting +with , would be zero, meaning +that the camera now keeps the same frame as last frame. Camera does not +jitter.

    +

    Here comes the question: what if the following target slows down, or +stops, or even turns back to the opposite direction and the camera still +remains the same residual to it?

    +

    It is quite a good question. But if we look carefully at the function +of , we will find this +situation will never happen. Let's rewrite here:

    +

    +

    This time, we do not constrain the value of , but at last frame, it's positive.

    +

    When gets smaller but still +positive, we observe the function gradually shifts leftwards, pushing +the root towards zero. This implies that the area gets +contracted and the probability of remaining the same residual gets +smaller.

    +

    +

    When is zero where the +following target stops, the current residual can be readily calculated +as , which closes the distance gap between the camera +of the following target. The ratio, which is calculated as , would be +devided by zero, outputting an infinite value.

    +

    When is negative, will be negative. The +ratio +now becomes negative, also beyond the range of .

    +

    We can implement this algorithm in less than 100 lines of code. You +should modify three files in the official Cinemachine source code +directory.

    +

    First is Predictor.cs. Add a ImprovedDamp +function:

    +
    public static float ImprovedDamp(float initial, float dampTime, float deltaTime, float bonus)
    {

    if (dampTime < Epsilon || Mathf.Abs(initial) < Epsilon)
    return initial;
    if (deltaTime < Epsilon)
    return 0;

    float tolerance = 0.05f;

    float alpha = Mathf.Log(bonus) * dampTime / kLogNegligibleResidual;
    float ratio = deltaTime / alpha;

    if (ratio <= 1.0f + tolerance && ratio >= 1.0f - tolerance)
    {
    deltaTime = alpha;
    }

    float k = -kLogNegligibleResidual / dampTime; //DecayConstant(dampTime, kNegligibleResidual);

    return initial * (1 - Mathf.Exp(-k * deltaTime));
    }
    +

    The input bonus is . Parameter tolerance is what you should set +as we've introduced +above.

    +

    In file CinemachineVirtualCameraBase.cs, add a new +function ImprovedDetachedFollowTargetDamp:

    +
    public Vector3 ImprovedDetachedFollowTargetDamp(Vector3 initial, Vector3 dampTime, float deltaTime)
    {
    GameObject go = GameObject.Find("Cube"); // Hard find our following target of interest, you should not do like this!
    Vector3 deltaDistance = new Vector3(100, 0, 0) * deltaTime; // Hard set the velocity, you should not do like this!
    Vector3 residual = initial - deltaDistance;
    Vector3 bonus =
    new Vector3(residual.x / (residual.x + deltaDistance.x + 1e-7f),
    residual.y / (residual.y + deltaDistance.y + 1e-7f),
    residual.z / (residual.z + deltaDistance.z + 1e-7f));

    dampTime = Vector3.Lerp(Vector3.Max(Vector3.one, dampTime), dampTime, FollowTargetAttachment);
    deltaTime = Mathf.Lerp(0, deltaTime, FollowTargetAttachment);
    return Damper.ImprovedDamp(initial, dampTime, deltaTime, bonus);
    }
    +

    This piece of code is very informal, and you should never write your +code like this. The purpose of this function is to get and . I reckon the correct +way to do this is to create a new (or two) variable in the +CinemachineVirtualCameraBase class and update it in each +tick. The code presented here is only for demonstration.

    +

    In file CinemachineFramingTransposer.cs, change the +called function for damping:

    +
    cameraOffset = VirtualCamera.ImprovedDetachedFollowTargetDamp(  // Original is DetachedFollowTargetDamp
    cameraOffset, new Vector3(m_XDamping, m_YDamping, m_ZDamping), deltaTime);
    +

    You could also try other components, not just +FramingTransposer here.

    +

    With the default tolerance=0.05, the result is shown +below.

    +

    +

    Camera jitters disappear. Note that the general fps is quite high +(around 400~500). This is because our scene is quite simple, containing +only a cube and a camera. In order to simulate a more real runtime game +situation, I place 20k cubes in the scene and now the fps is around 30, +but still unstable.

    +

    Below is the result when using the raw damping algorithm.

    +

    +

    Camera jitters more severely due to a generally lower FPS. What about +using the improved damping algorithm? Here is result with +tolerance=0.05.

    +

    +

    Just as expected, camera jitters do not show up. Let's try different +tolerances. How will a small tolerance help +alleviate jitters? Below is the result with +tolerance=0.01.

    +

    +

    Camera jitters occur again! This suggests that an excessively small +value cannot fully filter out actions that can lead to camera jitters. +Let's try our final experiment with tolerance=0.1.

    +

    +

    Camera jitters disappear, but the camera motion seems a little stiff. +These experiments show that an appropriate value of +tolerance to ensure the smoothness and robustness of the +camera.

    +

    Solution 2: adding low-pass +filter

    +

    Our improved samping perfectly solves camera jitters under unstable +fps, but it looks very stiff when it reaches the boundary of max damping +distance. Can we make it more realistic so that the object won't just +look stolid? Yes of course, we can add low-pass filter, or moving +average to our improved damping to achieve more smooth results.

    +

    Recall the algorithm of the improved damping: if , we just set to . +Instead of hard setting to , we introduce , the smoothed version +of the original delta residual . If holds, we calculate as an average of + and :

    +

    +

    which can be iterated through a recursive form:

    +

    +

    Note that gets +updated if and only if holds, +i.e., when the camera lies in the unstable area. The +use of is similar to +low-pass filters in the sense that they all filter out high-frequency +signals.

    +

    Below is s sample code implementation in file +Predictor.cs:

    +
    CurrentResidual = initial * Mathf.Exp(-k * deltaTime);
    ResidualDifference = CurrentResidual - PreviousRedisual;

    float result;

    float tolerance = 0.1f;
    float alpha = Mathf.Log(bonus) * dampTime / kLogNegligibleResidual;
    float ratio = deltaTime / alpha;

    if (ratio <= 1.0f + tolerance && ratio >= 1.0f - tolerance)
    {
    float beta = 0.001f;
    CachedDeltaResidual = (1 - beta) * CachedDeltaResidual + beta * ResidualDifference;
    result = initial - (CachedDeltaResidual + PreviousRedisual);
    }
    else
    {
    result = initial - CurrentResidual;
    }
    +

    Let's try it out! With damping time and , we can achieve the +following damping result with low-pass filter:

    +

    +

    Now the camera looks much more smooth and, flexible. What about +trying a smaller damp time, say, ? Here is the result:

    +

    +

    The result is ok but sometimes it's still jittering. It is because a +smaller leads to a larger and thus a larger chance to +cause jitters. To solve this issue, we can set a larger tolerance , or we can have a smaller +. We adopt a of and see how it performs.

    +

    +

    The camera now becomes smooth again.

    +

    We can measure this sort of instability more quantitatively. Below is +a graph plotting +during five seconds of camera trace with damp time . The original curve (in blue) +oscillates over time due to an instability of fps. The improved damping +method eliminates all the oscillation and makes the curve absolutely +plain. Empowered by low-pass filter, the curve becomes smooth without +loss of stability.

    +

    +

    Below is the graph with damp time . As can be seen, even with improved +damping, the camera still has a chance to vibrate, and the original +curve, oscillates much more intensely than with . Employing the low-pass filter +gives a much smoother and stable camera motion curve, as expected.

    +

    +

    Speaking of this, why can't we just soften our +improved damping assignment to where is a function of + parameterized by .

    +

    Assume +and , we first calculate ; +then calculate ; +last, we have . +For , we obtain . is a parameter controlling how fast the +value of grows from + to . The larger is, the larger mass will be +concentrated on the +side.

    +

    Below is the result with +and :

    +

    +

    Not bad! The soft version of improved damping really makes the camere +smoother and less stiff than the vanilla improved damping algorithm. The +follow plot also shows that with soft parameterization, the camera +trajectory is much more natural with neglectable amount of +oscillation.

    +

    +

    We also compare it to different and . Beolow is the result with and .

    +

    +

    A larger makes the camera more +stiff, but is still better than the original improved damping +algorithm.

    +

    Below is the result with +and .

    +

    +

    is less effective as the +magnitude of attenuation it applies to is not enough to compensate +for the osciallation the unstable fps brings about.

    +

    Let's try another damp time. The result with and shows as follows.

    +

    +

    When , the oscillation is +more severe, as we've already stated above. What about ?

    +

    +

    Better, but still not sufficient to mitigate the oscillation. Let's +try .

    +

    +

    Almost perfect. We can conclude that a smaller needs a larger to offset the intense jitters resulted +from unstable fps. Besides, you can combine the soft improved damping +method and low-pass filters to achieve a smoother transition.

    +

    Solution 3: continuous +residual

    +

    Okay, let's forget all aforementioned solutions and revisit our +residual update formula at the very beginning:

    +

    +

    Reformulate thie equation to the following form:

    +

    +

    where is the speed of the +camera's follow target, and is a more generalized form of the damping function . +Theoretically, it can represent any function of interest.

    +

    Now, regarding as a function +with respect to , we can seek to +obtain its derivative:

    +

    +

    We use the equality +because when you plug +into , you will get , implying .

    +

    What about derivatives with higher orders? We can calculate the +second-order derivative as follows:

    +

    +

    It is a nice form which bridges the first-order derivative and the second-order derivative +. In fact, for any +-th order derivative, it can be +recursively calculated as:

    +

    +

    Having all these derivatives, we can then expand using Taylor series and +calculate the difference to :

    +

    +

    Note that if we are still choosing as out +damping function , the +derivative of it with respect to will be and the value at zero will be .

    +

    In practice, we first decide how many terms in the coefficient term +should be taken in, and then sum them up and multiply with the velocity +term, the result of which is denoted by . The residual at the current +frame, can be readily computed as . To save +computation, we can first cache +up to a threshold, say , and +then using the formula of geometric series to efficiently compute the +coefficient sum.

    +

    To estimate its error, we use the Lagrange remainder:

    +

    +

    Decompose it:

    +

    +

    where +and as we assumed. We can +see that the error is asymptotically negligible with respect to , especially when is small.

    +

    Recall that +where and + is the damp time. If is large, say 0.5 or even 1.0, the +value of will be +somewhat small so that a decent precision can be reached within few +steps of expansion, i.e., a small +say 2 or 3 could satisfy camera stability. However, if is small, say 0.2 or 0.1 or even +smaller, the value of +would grow larger, and then a larger might be needed to reach our expected +precision. This is in accordance with our observation that a smaller + generally leads to a more +unstable camera trajectory. We will show this soon.

    +

    Let's first try and . Recall that is the maximum order of derivatives we +use to approximate the residual difference. means that we only use in the coefficient term. Here is +the result:

    +

    +

    Looks nice! What about setting ?

    +

    +

    Not much difference, but a little bit smoother. Let's try respectively with and . First comes .

    +

    +

    It's okay but it seems too fast when the cube comes back to +stillness. How about ?

    +

    +

    Now everything gets worked! Next, let's set smaller, which generally won't be used +in actual gameplay but as a test it's worth a try. We set and try different to see how they influence our camera +trajectory.

    +

    Here is the result with :

    +

    +

    Okay... a total mess. Try :

    +

    +

    Unfortunately, the cube always stays behind the camera. Now :

    +

    +

    Forget about it ... Let's try :

    +

    +

    Things are getting better! At least it does not shake anymore and +begins to stay at the right position. I bet is better:

    +

    +

    It's close! Last, we try :

    +

    +

    Finally, the camera disposes everything well. As we can see from the +process, a small requires a large + to reach the minimum acceptable +precision. I hope you never have the chance to use such a small , and if it happens, cache enough orders +of derivatives or it would be prohibitively expensive to compute at +runtime.

    +

    To further understand why this method solves the jittering issue, we +take a deeper look at the expression of derived above. +This is an ODE and we solve it out (proof left to the readers):

    +

    +

    Here I've expanded as . We cannot +directly use this explicit expression to calculate because there is no +correct time stamp when game is running. What we only have +is the previous frame's residual and the elapsed time at this frame +. And as the velocity may change over time, a closed-form of + cannor serve our purpose well. +We can only incrementally calculate camera residuals at each frame based +on what we currently have.

    +

    is a monotonic increasing +function, and of course, it's continuous. The continuity ensures that +the camera trajectory is always smooth and never jitters, if fps is +sufficiently high (over one thousand I suppose?).

    +

    For the original discrete residual, its velocity is:

    +

    +

    where is from +Lagrange's Mean Value Theorem. Note that I add a tilde symbol over to distinguish it from the one from the +continuos version above.

    +

    This is another ODE. We can solve it out (proof left to the +readers):

    +

    +

    Note that we solve the ODE with respect , the increment time rather than +the absolute time . So, we +introduce an initial value to control what the +initial value of residual is at this frame, is now the elapsed time for this frame +satisfying and .

    +

    The following graph shows that how the function changes with +different and . It can be +noticed that this function is very sensitive to the input , the elapsed time at this frame. A +small change of the input would significantly change the sign of , thus causing camera jitters. +We also notice that a smaller , +derived from a smaller , pushes +the function leftwards, which also makes it more vulnerable to +inputs.

    +

    +

    Below is a comparison between five damping algorithms introduced in +this article, including the original damping. Damp time is set to 0.2. We observe significant +stability improvement when using any of the four proposed damping +algorithms. You should be careful when choosing the most appropriate +algorithm because the situation on which you intend to use damping. How +unstable is your fps? What is the damp time ? How is the tracked object moving? You +should experiment with these algorithms and choose the one that best +suits your needs.

    +

    +]]>
    + + 游戏 - 相机 + + + 数学 + 随笔 + 计算机 + 相机 + Unity + Cinemachine + Damping + +
    + + Motion Matching -- 概念与发展 + /2022/03/27/08/38/ + 这是我在组内分享的一次关于Motion +Matching基本知识的介绍,放在此备份。

    + +

    Motion +Matching - 概念与发展

    +]]>
    + + 游戏 - 动画 + + + 数学 + 随笔 + 计算机 + 游戏 + 动画 + 深度学习 + 机器学习 + +
    + + A Brief Note on Version Control and Project Organization + /2022/11/21/16/03/ + This is a brief note about the e-book Version +Control and Project Organization Best Practice Guide present at +Unite 2022. In this book, we can learn fundamental concepts of version +control and some best practices for organizing a Unity project. If you +are new to Unity, or you prepare to set up a larger scale Unity project, +this may be what you need.

    + +

    Foundational concepts

      -
    • 代理能动性:人们努力以某种手段争取那些拥有资源或技能的人按照他们的意图行动,,以协助他们获得期望的结果。 +
    • Version control enables you to keep a historical +track of your entire project. It facilitates your collaboration with +your team through trackable and revertible commits organized in the form +of timeline.
    • +
    • Why use version control
        -
      • 游戏攻略。
      • -
      • 论坛讨论。
      • +
      • Useful for making experimental changes
      • +
      • Easy iteration
      • +
      • Avoid conflict
    • -
    -

    集体能动性:粉丝作品

    -
      -
    • 文本在粉丝中被认为式一个可以被集体改变、改编和重写的过程,一个未竟的事业,而非静态对象。
    • -
    • 在粉丝艺术家的创作过程中,我们能够看到所有的能动性形式。在某种层次上,帖子表现出个人发挥其能动性和培养个人能力时所带有的焦虑、压力和挑战。粉丝们在对彼此的反馈、建议和专业技能表示支持和学习的过程中,利用了代理能动性。
    • -
    -

    结论

    -
      -
    • 游戏在某种程度上要求甚至依赖玩家能动性的积极发挥。对玩家能动性的关注将引导我们重新评估对以下问题的常规臆断:游戏是什么?谁在生产它?怎样生产?
    • -
    -

    电影、改编与电脑游戏

    -

    恐怖类型——从电影到游戏

    -
      -
    • 恐怖类型携带者“所有口头叙事的印记:主题和母题的自由转换、原型人物和背景设置,以及不断累积的续集、翻拍和模仿。某种意义上,在这个领域当中,没有原创,没有真实的或正确的文本,而只有变体。”
    • -
    -

    改编与《突变怪物》

    -
      -
    • 不可预知的威胁乃至有时无法辨识的敌人及其带来的焦虑,正是为玩家提供的挑战,玩过经过训练即可将之战胜。在从电影到游戏的移植中,其功能和意义均发生了改变。
    • -
    • 游戏设计中最为重要的,是对空间和空间当中物品的设置与安排。
    • -
    • 为已经开发出来的游戏增加一个额外关卡是一回事,而尝试去想象一个尚不存在的游戏的某个关卡是另一回事,后者要困难得多。
    • -
    -

    人物塑造与过场动画

    +
  • Centralized vs. distributed version control
      -
    • 一款好的游戏需要为玩家提供有意义的选择,以及相应可觉察的成果。《突变怪物》的叙事性元素给游戏的目标、任务和障碍提供了背景。
    • -
    • 恐怖游戏的操作应当足够简单,因为这会在相当程度上将玩家限制在其所处的环境中,增加游戏的悬念。
    • -
    • 过场动画分为两种:预制的和实时的。必须对带有预制过场动画的游戏进行周密计划,以避免其对游戏世界的真实感造成破坏。
    • -
    -

    改编——从游戏到游戏

    +
  • Centralized: repository is resided in a dedicated server, and +changes are fetched from and sent to the repository directly. To avoid +conflicts, users can lock files for modification, which is known as +checking out the file.
  • +
  • Distributed: users have local copy of the project and submit changes +whenever they want without always working on the latest files like on a +centralized system. But it costs a lot of space to store the entire +history changes.
  • + +
  • Typical workflow
      -
    • 目标是开发一个具有足够普适性的游戏引擎。
    • +
    • Centralized +
        +
      1. Update your working copy with changes from the server
      2. +
      3. Make your changes
      4. +
      5. Commit your changes to the central server
      6. +
    • +
    • Distributed +
        +
      1. Pull any remote changes into your local repo
      2. +
      3. Make changes
      4. +
      5. Commit changes
      6. +
      7. Push changes back to the remote repo
      8. +
    • +
  • -

    游戏与性别

    -

    作为再现的游戏

    +

    Best practices +for organizing a Unity project

      -
    • 有研究者认为,女性对电脑游戏的疏离主要是因为其再现性因素——游戏中女性化身的外形。
    • -
    • 在很多游戏中,被特别标志的或被边缘化以至于缺席的性别,都是女性。
    • -
    • 如果将电脑游戏视为一种文本,那么将玩家定义为一种性别主体并不可行。
    • -
    -

    娱乐性与再现性

    +
  • Folder structure
      -
    • 一些游戏强调视觉呈现和故事讲述方式——它们在引人入胜的细节中呈现游戏的场景和角色,我们将之称为游戏的再现层面。
    • -
    -

    性别、游戏与语境

    +
  • Recommendations:
      -
    • 游戏开发商或其发行商如果要争取更多的女性玩家,就要面临失去现有男性玩家的危险。
    • -
    • 那种认为女性对电脑游戏不感兴趣的想法和剥夺女性玩游戏权利的做法已经过时了,尽管某些社会因素和文化因素还将因用户的性别而持续对其产生影响。
    • -
    -

    游戏分析的实践

    -

    定义游戏

    +
  • Document your naming conventions and folder structure.
  • +
  • Be consistent with your naming convention.
  • +
  • Don't use spaces in file and folder names.
  • +
  • Separate testing or sandbox areas.
  • +
  • Avoid extra folders at the root level.
  • +
  • Keep your internal assets from third-party ones.
  • + + +
  • The .meta file: it holds information about the file +which it is associated with, e.g., Textures, meshes, audio clips that +have particular import settings.
  • +
  • Naming standards:
      -
    • 目标、障碍、资源、奖赏和惩罚等是电脑游戏的一些重要元素,同时还有一些可获得的不同种类的信息——不管是对于所有玩家,还是对于个别玩家,抑或对于游戏本身——以及这些信息逐渐显露的诸多方式。
    • -
    • 我们也关注到其他有助于探索游戏要素多样性的理论研究方法, +
    • Use descriptive names, not abbreviate.
    • +
    • Use Camel case/Pascal case.
    • +
    • Use underscore sparingly.
    • +
    • Use number suffixes to denote a sequence.
    • +
    • Follow document naming.
    • +
  • +
  • Workflow optimization:
      -
    • 例如,可以探究游戏当中的奖赏与惩罚(游戏的“经济体系”)或游戏当中玩家可控因素与不可控因素之间的制衡关系。
    • -
    • 同样,也可以探索游戏中不同类型的障碍物
    • -
    • 还可以分析不同类型的游戏规则
    • -
    • 它们为我们发现一款游戏的与众不同之处,并促使我们继续思考这种差异是有意义的创新还是对规范的偏离。
    • +
    • Split up your assets: break levels into smaller scenes, using +SceneManager.LoadSceneAsync; break work up into Prefabs +where possible.
    • +
    • Use Preset to save asset settings.
  • -
  • 所有的定义都聚焦于被我们所称的游戏的娱乐层面或“游戏系统”,是它们决定一款游戏是不是可玩,以及游戏活动所遵循的限制。【游戏系统决定了游戏的定义。】
  • -
  • 对电脑游戏的分析既要关注“娱乐性”,也要关注“再现性”,以及二者之间的关系。
  • - -

    游戏类型

    +
  • Code standards
      -
    • 在一个典型的动作冒险游戏中,我们通过扮演游戏内设定的化身来进行游戏,该化身通常会依循特定的游戏顺序克服已被设定的的重重困难。游戏目标通常相当明确,其经济体系一般与可量化的特征相关,比如生命值和弹药。游戏玩法中最核心的是速度和精确性。玩家会在游戏过程中学习一些技巧,但化身却不会成长。【游戏路线是既定的,游戏目标是明确的,游戏玩法是速度和精确度。】
    • -
    • 与之相反,RPG游戏的主角可能是玩家通过一系列选择构建出来的,而且围绕该角色通常会有一个具有特定技能的辅助性团队。玩家可以自由地在游戏中探险,展开各种探索,并且可以不用遵照预设地步骤战胜游戏中的各种障碍。虽然游戏最终目标可能非常明确,但通常会有一些与最终目标未必相关的支线目标。以经验值和物品清单呈现出来的经济体系十分重要,但它更倾向于随玩家的表现而浮动。玩游戏的关键在于策略而非速度,而游戏体验往往具有更多反思性。【游戏路线是未定的,游戏目标是多样的,游戏玩法核心是策略。】
    • -
    • 这两者之间的关键性差异与游戏系统有关,而非在再现层面。
    • +
    • Decide a code standard and stick with it.
    • +
    • When using namespace, break your folder structure up by the +namespace for better organization.
    • +
    • Using a standard header.
    • +
    • Using script templates by creating an +Assets/ScriptTemplates folder.
    • +
    • You can also use your own keywords and replace them with an Editor +script implementing the OnWillCreateAsset method. +
      // /*-------------------------------------------
      // ---------------------------------------------
      // Creation Date: #DATETIME#
      // Author: #DEVELOPER#
      // Description: #PROJECTNAME#
      // ---------------------------------------------
      // -------------------------------------------*/
      using UnityEngine;
      using UnityEditor;
      public class KeywordReplace : UnityEditor .AssetModificationProcessor {

      public static void OnWillCreateAsset (string path)
      {
      path = path.Replace(".meta", "");
      int index = path.LastIndexOf(".");
      if (index < 0)
      return;

      string file = path.Substring(index);
      if (file != ".cs" && file != ".js" && file != ".boo")
      return;

      index = Application.dataPath.LastIndexOf("Assets");
      path = Application.dataPath.Substring(0, index) + path;
      if (!System.IO.File.Exists(path))
      return;

      string fileContent = System.IO.File.ReadAllText(path);

      fileContent = fileContent.Replace("#CREATIONDATE#", System.DateTime.Today.ToString("dd/MM/yy") + "");
      fileContent = fileContent.Replace("#PROJECTNAME#", PlayerSettings.product-Name);
      fileContent = fileContent.Replace("#DEVELOPER#", System.Environment.User-Name);

      System.IO.File.WriteAllText(path, fileContent);
      AssetDatabase.Refresh();
      }
      }
    • +
  • -

    叙事与游戏

    +

    Version control systems

      -
    • 沉浸分为感知上的沉浸与心理上的沉浸,卷入则是玩家被迫在游戏中采用一种更为审慎和具有反思性的态度。
    • -
    • 沉浸和卷入并不互相排斥,二者间的摆动在游戏“心流”体验的产生中处于核心地位。所谓心流体验,就是玩家感到自己此刻“处在巅峰状态”,这种及其满足和欲罢不能的感觉部分由玩家在沉浸和卷入之间的转换所触发。
    • +
    • Git: Fork, GitKraken, VS Code, VS, SourceTree, Sublime Merge.
    • +
    • Perforce (Helix Core): see here +to learn how to integrate Perforce into Unity.
    • +
    • Apache Subversion (SVN)
    • +
    • Plastic SCM: see here to learn more +about Plastic SCM.
    -

    穿越游戏空间

    +

    +

    Settings up +Unity to work with version control

      -
    • 等距视角的RPG游戏更多依赖心理上的沉浸,而《寂静岭》中的移动视角和3D效果唤起玩家感知沉浸。
    • -
    -

    游戏动力学

    +
  • Editor project settings
      -
    • 为什么游戏攻略大多采用第二人称祈使语气?因为游戏攻略大都紧密围绕游戏系统撰写,几乎不会注意再现层面,攻略是冷静的和技术性的。粉丝撰写的小说和诗歌都着墨于游戏的再现层面,倾向于借鉴游戏的过场动画,而非游戏本身的互动性元素。
    • +
    • Perforce: Edit -> Project Settings -> Version Control -> +Mode.
    • +
    • Plastic SCM: click the Plastic SCM icon in the toolbar on the top +right in Unity Editor.
    • +
  • +
  • What to ignore: Do not commit the Library folder, as +well as the .exe or .apk files.
  • +
  • Work with large files: teams prefer a centralized workflow where +large binary files would only on a central server with individual users +only accessing the latest version on their machines, rather than a +distributed one where many copies of historical files are stored on +local machines. If using Git, be sure to include Git LFS.
  • -

    走向线上

    +

    Best practices for version +control

    +

    Some suggestions you may need to make teamwork more efficient:

      -
    • 玩家玩在线游戏的动机与游戏的娱乐层面和再现层面都有关系。 +
    • Commit little, commit often.
    • +
    • Keep commit messages clean.
    • +
    • Avoid indiscriminate commits. It is important to understand that you +should only commit what you have changed in the project.
    • +
    • Get the latest
        -
      • 再现层面的动机包括操纵视觉形象、创建角色和生成叙事。玩家构建化身的方式将折射出其现实生活的某些方面,或其内在愿望和幻想,而参与游戏的模式反映出不同的社会动机。
      • -
      • 娱乐层面的动机则更多和游戏活动本身直接相关,涉及竞争、规则和游戏目标。
      • -
      • 此外还有公共动机,即玩家之间的互动,这是一种多模态呈现的现象。
      • +
      • Git: pull -> edit -> pull -> commit -> pull -> +push.
      • +
      • Perforce: get latest -> check out files -> edit -> +submit
    • -
    -

    游戏中的社会生活

    -
      -
    • 在团队协作中,游戏的娱乐层面是主要的。
    • -
    • 游戏具有三个能动性:个人能动性、代理能动性、集体能动性。
    • -
    -

    生产游戏、生产意义

    +
  • Know your toolset
      -
    • 游戏设计者最为关心的问题,正是游戏的娱乐性和再现性之间的相关性和互联性。比如,角色具有再现性和娱乐性,过场动画也有娱乐性从(提供了战略上必不可少的信息)。
    • +
    • Git: UI client
    • +
    • Plastic SCM: Gluon
    • +
    • Perforce Helix Core: built-in Unity Editor tools
    • +
  • +
  • Feature branches and Git Flow: main, hotfix, release, develop, etc. +Both Plastic SCM and Perforce have automated tools to help manage +merging branches back into mainline. Plastic SCM does this with the help +of MergeBot, +and Perforce uses Helix Swarm for +managing code reviews that can also be set up with automated +testing.
  • +

    The biggest takeaway is the importance of clear team communication. +As a team, you need to agree on your guidelines

    ]]>
    - 游戏 - 游戏理论 + 随笔 随笔 - 游戏 + 项目管理
    @@ -3840,2447 +3296,2991 @@ B-spline (NURBS) - ComponentCameraSystem - A Simplified Camera System for You to Create Plentiful Gameplay Camera Movements and Effects - /2023/02/09/23/21/ - ComponentCameraSystem is a simplified, extensible -and designer-friendly camera system for Unreal Engine. It enhances the -built-in spring arm and camera components in native Unreal editor across -a wide variety of common gameplay camera behaviours such as keeping a -target at a fixed screen position, moving on rail, and resolving -occulusion in complex occasions, enabling you to easily create plentiful -smooth camera movements and effects within only few minutes. Go to the -Documentation -for more details.

    -

    Currently ComponentCameraSystem supports Unreal -Engine versions >= 5.0. So before using this plugin, please upgrade -your project to Unreal Engine 5.0 at its minimum version -requirement.

    -

    You can buy this plugin at Unreal Marketplace. -Persistent upgrade will be made to make it more stable and support more -features.

    -]]>
    - - 游戏 - 相机 - - - 计算机 - UE - 相机 - 游戏 - 插件 - -
    - - 无穷连根式求极限的充要条件 - /2020/09/09/18/52/ - 在刷习题集或者考试的时候我们经常会遇到诸如或者的极限求解或极限存在性证明。解决此类问题的方法有很多,但都可以归结为一点:缩放。要么是两端缩放然后夹逼定理,要么是证明有界然后两边取极限。本文记录此类问题极限存在的一个充要条件,以供参阅。

    + 《Assassain's Creed Odyssey》玩后感 + /2020/09/08/18/39/ + 115小时,主线+两个DLC通关。奥德赛是一个优秀的游戏,但不是神作,在走向RPG的道路上是系列作品的里程碑。人物塑造方面,奥德赛无疑是成功的,由于我选择的是温情 +路线,所以我看到的马拉卡的内心是无比渴望家庭的温暖的,正如奥德赛音乐集的“Odyssey(Modern +Version)”中的歌词"Travel in path alone, back to the warmth of +home"一般,踏在异乡的每一步无一不通往家。本作两个主要配角Phoibe和Brasidas,都令人印象深刻。在画面上,优良的美工沿袭了育碧式BUG,有可能在欣赏风景的时候会卡到墙里面,有时候还挺扫兴的。值得一提的是,奥德赛对希腊风情的刻画非常优秀,每一个同步点都可以疯狂截图作壁纸。本作比较难受的地方是剧情,三条主线除了家庭线比较完整之外,另外两条主线都虎头蛇尾,过多的无用支线严重稀释了主线的紧凑感,让人在玩到一半之后想去玩巫师三。DLC1的整体剧情我个人比较喜欢,但是一些细节处理尤其不妥,动机不足导致DLC1评分降低。DLC2流程很长,总体来说冥界剧情好于亚特兰蒂斯剧情好于极乐世界,不过对神之领域的描绘总体来说很精彩。如果说我对11.10发售的英灵殿有什么值得期待的话,可以用几句话总结:剧情不要拉跨,任务尽量优化;BUG可以少些,卡墙不动尴尬;风景依旧如画,壮汉赶紧来吧!

    -

    Ramanujan's Problem

    -

    这类问题最著名的是拉马努金(Ramanujan)所提出的恒等式:

    -
    -

    证明:

    -
    -

    这个等式的证明是简单而有趣的:

    -

    -

    同时,Ramanujan还断言下面的结论:

    -

    -

    这个的证明也是容易的。首先把上式和(1)式比较,就发现上式以3为上界,并且由单调性可知,其极限是存在的。为了证明的极限就是3,我们证明:对任意的,都存在,使得所有的都有

    -

    现在任取,令,故,故有:

    -

    -

    乘进去,就有:

    -

    -

    由于,且存在,当时,有

    -

    -

    把这些全部带入(2)式,就可以证明:

    -

    -

    从而完成证明。

    -

    Polya's Criterion(Polya准则)

    -

    在考虑普遍情况下首先来观察一些特例,一个典型的特例就是形如的无穷根式,每个根号的次数都是,或者幂次都是。对于来看,控制它的幂次是,对于来说,控制它的幂次是,对于来说,控制它的幂次是。假设这个无穷根式极限存在,那么我们关心的肯定是足够大时,它被什么控制,显然是及控制它的的幂次。所以,一个合理的猜测是,如果极限存在,那么该根式就收敛。下面我们将看到,这个猜测已经非常接近“真相”,甚至是真相的一部分。

    -

    Description

    -

    设序列,则可以用下述条件判定:

    -

    -

    上述准则还可以进一步推广为:

    -
    -

    序列收敛的充要条件是: -

    -
    -

    注意到,这个极限可以取有限数或者负无穷。我们将在陈述下面的定理一之后进行证明。

    -
    -

    (定理一) 序列收敛当且仅当存在有限上极限 -

    -
    -

    首先证明必要性,即假定收敛。因为,故一定是有限的,得证。

    -

    再来证明充分性。假定,则存在使得对所有,有,因此。从而有:

    -

    -

    同时又因为

    -

    -

    从而有,又由的单调性知收敛。

    -

    到此为止,我们发现定理一和开始我们的猜测是非常相似的,只是定理一只需要上极限,这比我们的猜测更加宽松。下面我们利用此定理证明Polya's -Criterion.

    -

    Proof of Polya's Criterion

    -

    时,存在,当,也即,由定理一收敛。

    -

    时,存在某个,对某些无限的,使得,也即。因此,对这些而言:

    -

    -

    因此.

    -

    最后考虑

    -

    收敛,则有限,即存在对所有成立,因而。此时若,则

    -

    -

    而当时,按照约定有,则综上公式(3)的必要性得证。

    -

    同时公式(3)也是充分的。假定条件成立但不收敛,则由定理一,则对任意的,有充分大的使得,从而,于是有

    -

    -

    这说明上极限是无穷大,与假设矛盾。充分性得证。

    -

    Examples

    -

    例一

    -

    现在我们考虑一个序列,当的时候,它的上界是,而后者我们上面已经证明了它的上界是2。现在我们考虑的情景。此时有:

    -

    -

    所以我们证明了,对,序列都是收敛的,并且没有使用定理一

    -

    例二

    -

    现在考虑下述恒等式:

    -

    -

    于是可以立即得到:

    -

    -

    现在令,就有:

    -

    -

    替换,有:

    -

    -

    两边约去2,就有:

    -

    -

    例三

    -

    由余弦二倍角公式可知:

    -

    -

    此时令,就有:

    -

    -

    所以可以立刻得到下式的极限:

    -

    -

    Herschfeld’s -Convergence Theorem (Herschfeld收敛定理)

    -

    Polya's Criterion只考虑了指数为的情况,对于更加普遍的情况,即形如的序列,Herschfeld’s Convergence -Theorem给出了一个其收敛的充要条件。

    -

    Herschfeld’s -Convergence Theorem 告诉我们了一个无穷根式收敛的充要条件:

    +

    剧情:整体精彩,细节拉跨

    +

    自奥德赛发行以来,“剧情”一直是被玩家广为诟病的一点,但是在做完所有任务(包括两个DLC)、体验完所有剧情之后,我倒是觉得奥德赛的剧情整体上非常优秀,但是和大多数玩家一样,我也想吐槽其中非常多的细节,我思考了一下,这大概是出于一种“恨铁不成钢”的心理。明明奥德赛的剧情可以做得非常棒,可以几无瑕疵,可以触及巫师三,但是阿育就是把这么一个差一点点就堪比完美的剧本甩到玩家脸上,香还是香的,但就是不情愿,被迫品尝一桌盛宴中编剧埋下的一坨坨屎。

    +

    世界观

    +

    奥德赛的世界观是建立在希腊伯罗奔尼撒战争期间,但显然,奥德赛不完全是一部纪实游戏,战争只是游戏的大背景,也是游戏开始的一个引子而已,到了后面,游戏的进展其实和战争已经没什么太大关系了。在这个大背景下,著名的时代人物,如伯里克利、苏格拉底、布拉西达斯都会悉数出场并成为推进主线过程中重要的人物,而另一大群体,“神教”,正贯穿了整个主线的发展。主角的冒险由神教展开,也从神教结束,神教作为核心暗线而存在,我们把它称为神教线。而剧情的实际主线则是马拉卡找家人的旅程,在途中,主角会踏遍希腊,经历诸多精彩的故事,最终找到家人,在若干年后再次相聚,我们把它称为家庭线。除此之外,奥德赛中还引入了很多神话元素,比如传奇动物、传奇生物、神之领域(主要在DLC2),它们以“神器”联系起来,游戏进行到中后期会出现一条新的主线,也就是让主角去搜集神器,我们称之为神器线

    +
    +奥德赛的地图很大 + +
    +

    所以,总的来说,奥德赛的世界观就是在希腊伯罗奔尼撒战争的时代大背景下,以家庭线为明线,以神教线为暗线,以神器线为辅线而展开的。在游戏进行过程中,主角会见证真实的历史,会体验各地的风俗,会欣赏希腊的风光,会体验人生的波折。奥德赛的世界观即是如此的宏大,在游戏策划的精心编排下将希腊的大观悉数摆在我们面前,同时又让玩家以小人物的视角代入到如画的场景中,如此结合,让玩家一旦进入到角色里,就很难立刻停下来。

    +

    然而,相比起源把一桌菜一股脑全给你端上来又不介绍菜品让你细细琢磨、品尝,奥德赛更像是古典西餐厅里的一盘盘端上精心调制的菜品并为你介绍菜的制作方式、选用食材、食用步骤,甚至还要告诉你在品尝之前要沐浴更衣精心打扮,这就会产生下面要说的一个问题,大而不精,野心太大但却没有把握住剧情开展的主次,导致剧情上连贯性的断裂,这进而引发一个很严重的问题:游戏进展到后期同质化严重,玩家的游戏热情被大幅度削减。就好比前几道菜都详细介绍,食客会觉得很高端很优秀,但是如果有几十道菜,每个都如此呢?食客就会厌烦。

    +

    任务

    +

    剧情是通过任务推进的。任何一个游戏,都有任务,完成了任务,尤其是主线任务,才能顺利推进到下一个剧情节点。可以说,任务是推进主线的首要手段(并非唯一,比如在奥德赛中,满屏幕的问号“?”也是一个手段)。

    +

    上面我们已经简单说了,奥德赛的主线可以分为三条:家庭线、神教线和神器线。家庭线和神教线是从头贯穿到尾的,二者并行,一明一暗互为依托,这样的设计是很好的:它既不会让游戏快速陷入单线游戏的乏味,也能营造剧情上汹波暗涌的气氛,很快抓住玩家让玩家进入角色。这里值得表扬的是本作家庭线和神教线的融合是非常到位的,二者不但是并行关系,而且是交叉关系,如同螺旋前进的两条线一样,共同推动了游戏剧情的发展。但是,第三条线神器线的出现大大打破了这一节奏,甚至是原本剧情的美感。在家庭线达到关键节点找到母亲后,本来以来父亲的出现会解释玩家所有的疑问,但是没想到,老父亲一出场就让玩家莫名其妙地搜集神器,没有半点前因后果的交代;在找到神器之后,又莫名其妙出现了古代与现代的时空错乱和穿越,我们的主角马拉卡又莫名其妙地活到了现代穿上了西服最终溘然长逝。说实话,我当时玩到这一段的时候几乎是快进过的,没有丝毫欣赏剧情的欲望。这一条线从一开始,到最后,只能用四个字去总结:说的是啥?尽管这一主线流程很短,但它的出现极大割裂了另外两条主线的连续感,也造成了马拉卡精分的错觉,让玩家搞不明白奥德赛的时间线究竟是如何排布的。

    +

    当然,家庭线和神教线不是没有问题,而且在很多细节之处都有槽点。比如家庭线的一大槽点就是,如果你走的是大团圆路线,你就会发现马拉卡过于圣母(没错,就是我)。诚然马拉卡是一个呆萌渴望家庭温暖外表冷漠内心火热的失足少年,但是当自己的妹妹残忍地杀害基友Brasidas,造了无数的孽后,马拉卡依然选择了原谅她,最后还能和继父、表弟在一个桌子上吃饭,我的内心无疑是充满了问号。你可以说,这是你自己选择的剧情啊!对,但是当玩家选择了这个剧情之后,我们会明显感觉到这个剧情是不尽合理的,也就是说,制作人在设计这一剧情路线的时候就没有仔细推敲其合理性,所以这个锅还要阿育来背。神教线的槽点就更多了,各种奇葩的神教成员(包括那个自爆身份好像老子不怕你的那个)一个接一个,最后鬼魅那段单口相声真的是漏洞百出,竟然还可以选择相信她,而且无论你选择什么,神教线都会到此为止,那又何必再做选项呢。

    +
    + +
    鬼魅的最后一个线索才是“女性”,不然很好猜
    +
    +

    除了主线之外,奥德赛的一大尤为突出的问题是,支线任务过多,而且过于无聊。在奥德赛中,除了标有黄色感叹号的关键支线之外,其他大部分支线都是在端茶倒水、洞穴找人、团灭兵营,基本上可以概括为:“我丈夫/妻子/兄弟/孩子不听我话跑出去不见了/被强盗抓住了,他可能在某某树林/洞穴/兵营里,你能帮我找到他吗”。刚开始玩用来点亮地图确实还不错,而且可以熟悉技能,提高操作,但是当游戏进行到中后期时,满屏幕的感叹号让你没有丝毫欲望去清空,因为你知道打不打都无所谓,对主线毫无影响,给的物品也毫无吸引力。也许制作者的初衷就是让玩家在打完主线之后再慢慢清支线,但实际上,对于一部分强迫症玩家来说,满屏幕没清又强行按捺自己去清欲望的想法的确是非常痛苦的,最终的归宿可能只有隐藏地图标记才能解救他们。尽管对总的剧情没有太大影响,然而过多的冗余支线就好比蒙娜丽莎嘴角沾了米粒,赫拉脸上长满痘痘,人还是那个人,但味道总觉得变了。

    +

    但另一方面,也不得不表扬一些支线的确做得很有意思。比如“失落的希腊神话”系列任务就很有意思(尤其是“倒霉的一天”),此外,雅典区的一系列支线也很有特色(苏格拉底与雅典炮王的任务),还有一开始瘟疫的支线,这些支线要么本来在内容上非常丰富、有趣,要么会影响后续的剧情,这就回到了支线的作用:需要给予玩家一定的反馈。如果做完一个支线,玩家什么都没有得到,什么反馈都没有,那做这个支线干什么呢?看风景吗?所以,我认为如果把神器线做成一个系列支线,那效果可能就会好很多。如果删掉其中80%的无用的没有任何反馈的支线,说不定奥德赛的IGN评分又会更高。

    +

    可以看得出来,奥德赛在任务系统上有浓重的借鉴巫师三的痕迹,主要体现在剧情选项和支线系统。支线系统我们已经上面已经说了,和巫师三一样都有很多支线,但是巫师三的支线是它被称为神作的原因,奥德赛的支线只有东施效颦的副作用,究其原因,还是二者对支线作用的理解不同。

    +

    剧情可选是奥德赛在整个刺客细条系列中的一大突破,也是效仿巫师三最典型的一个要素。为什么我们需要可选剧情?因为这可以给玩家更多的自由度,让玩家能够更好地带入剧情,玩家需要为每一个决定负责,从而就要深思熟虑,剧情选择引发的蝴蝶效应足以在游戏结局的时候升华玩家对整个游戏的评价和感悟。巫师三做到了,奥德赛没做到。这有几点:

    +
      +
    • 奥德赛大部分选项不会对后面的游戏剧情有所影响;
    • +
    • 可选项非常少,玩家可能面对哪个都不想选但又不得不选的尴尬境地;
    • +
    • 选择之后的剧情推进和想象的不一样。
    • +
    +

    从我个人的游戏体验来说,上面三点是奥德赛在“剧情开放”尝试中最为突出的问题,我相信也是大部分玩家承认的问题。奥德赛的最终结局只有几种,而决定这几种结局的选项在几十个小时的游戏中只有寥寥几个地方,那其他的无数选项的作用是什么呢?第二,奥德赛每次可选的大概在3~5个选项之间,而给出的所有选项有时候又都非常智障,明明哪个都不想选却偏要选一个,这对玩家非常不友好,有种强行喂食的感觉。第三,剧情总是朝着意料之外的发展。比如有个任务是关于贩夫的未婚妻,这个时候就不能骗她说贩夫被干掉了,否则直接导致任务失败;而有的时候又需要你假惺惺地欺骗一下NPC才能完成任务,这种任务设定毫无规律可循,遇到了大概率就是一脸懵逼然后呵呵一笑。综上,剧情上的自由可选,就奥德赛的表现来看,反而是禁锢了玩家的自由度,看似让玩家有更多决定剧情走向的权利,但实际上是画地为牢,无论怎么选,都圈定在了编剧预先定义好的条条框框里了。显然,这是一种非常不成熟、宁可没有的设计。当然了,作为向巫师三学习并处于刺客信条系列发展史的一部过渡作品,奥德赛牛刀小试也无可厚非,只是希望英灵殿能够在这上面进行改进。

    +

    最后来说一下两个DLC。网上普遍对DLC1的剧情诟病颇深,对DLC2倒是赞誉更多,不过我个人倒是觉得,DLC1的剧情高度比DLC2更高,但是下限也比DLC2低,而DLC2发挥整体非常平稳。但从剧情上讲,我个人更喜欢DLC1,尽管它有非常突出的硬伤。

    +

    DLC1的剧情整体上非常棒,前提你玩的是Alexios(据说Cassandra体验非常不好)。游戏流程不长,十个小时以内可以打完,但是任务节奏很紧凑,内容相对丰富,关键是剧本很给力,讲述了马拉卡遇到真爱但是又被命运无情摧毁的故事。在这个DLC里,我看到了马拉卡内心的脆弱,尤其是他对家庭温暖的渴望与期盼。其实,在玩游戏本体的时候,我们会经常听到马拉卡感慨自己的命运,我印象中比较深刻的是“要是我不离开凯法隆尼亚岛就好了”,再回忆片头马拉卡独自坐在房上抚摸着断矛,他一定非常渴望再见到亲人吧。虽然在凯法隆尼亚岛上,他可能永远也见不到亲人,但是,有Markos,有Phoibe,还有岛上其他的伙伴,这样的小日子,不也很美好吗,正如他与DLC1的女主相遇相知相爱后,与岳父大流士、自己的孩子四人一起生活在村里,平淡而温馨,甜蜜又幸福,不也正是马拉卡一直想要的生活吗。做一个漂流在外四海为家的佣兵并不可怕,可怕的是这样的人却时刻想要有一个稳定幸福的小家庭,这简直是一种无法企及的奢望。当上古维序者干掉女主,马拉卡在火海中呼唤着她的名字时,当马拉卡来到家门口的她的坟墓前时,当马拉卡之后到他们相知的三个地方寻找纪念物时,当最后马拉卡把孩子托付给岳父大流士看着他乘帆远去时,当最后的最后马拉卡仍旧时孤身一人时,我心中的确是被深深触动了。这是我非常喜欢DLC1的点。

    +

    但是也正如上面所说,DLC1的上限很高,下限也很低,主要仍然体现在细节不到位。比如火海救岳父的情节,作为一个久经沙场的佣兵,他应该是完全能够意识到将妻子儿子丢在船边会有什么后果,就算不能两边兼顾,也应该知道孰轻孰重。再有最后送走儿子的桥段结束得过于仓促,上古维序者这个组织的行为动机也不够充分,在很多地方,玩家是没有办法选择剧情的走向的,也就不得不被迫喂屎,这也是DLC1被广大玩家吐槽的原因所在。但我个人认为,DLC1仍然是瑕不掩瑜的,对于部分玩家而言依旧有非常高的可玩性。

    +
    + +
    这一段还是非常感人的,我流下了130滴眼泪
    +
    +

    相比之下,DLC2的流程就非常非常长了,目前需要30个小时左右才能全部通关。DLC2分为三个章节,每个章节的流程都很长,相互独立但又通过“神话”这一线索串联起来,大概是寻找亚特兰蒂斯并探索神杖的用法。这个DLC把正作的神器线联系在了一起,在希腊和现代两个时间点之间不停穿越,目的就是要给玩家解释这个神器是怎么一回事儿。说实话,我也不知道他解没解释清楚,反正我基本都是快进过了,因为实在是过于无聊。

    +

    在剧情上来看这个DLC没啥有意思的,我总结一下,第一个章节是神秘四角恋上演姐妹撕逼大战,第二个章节是冥王海王打赌以虐待死者为乐,第三个章节是主角化身正义使者最终颠覆亚特兰蒂斯政权。总之就是云里雾里、莫名其妙。不过值得表扬的是,每个章节的特色地图都非常不错,极乐世界的花花草草,冥界的幽暗阴森感,亚特兰蒂斯的宏伟高科技,给人耳目一新的感觉。我印象最深的还是冥界的Phoibe剧情和Brasidas基友剧情,还是有点感人的,到了最后也特别想痛扁冥王。极乐世界充当双面间谍的感觉也不错,只是亚特兰蒂斯过于平平无奇,除了最后的BOSS有点恶心之外,也没有什么比较让人能够记住的点了。

    +

    这里特别吐槽一下冥界Brasidas的剧情,实在非常无语和狗血,把编剧的圣母心态体现得淋漓尽致。作为一个从小接受战士教育的将军,在战场杀敌是本分;当敌人杀向你,举起武器反击是本能;只是推开挡路的女人而没有将其杀死,是本心;无论从哪个角度讲,Brasidas都没有做错,战争也好,斯巴达的荣耀也好,还是出于自己的意志也好,Brasidas的做法都是无可非议的。但是,编剧强行要通过这个故事去虐Brasidas,本来都死得那么惨了,还要让他做出痛苦的抉择,实在是非常过分。在这里,我心疼基友三秒钟。

    +
    +基友太惨了 + +
    +
    +我恨这个女人 + +
    +

    画面:时代顶尖,BUG难掩

    +

    奥德赛延续了起源优秀的画面,在刻画希腊风光上做到了无出其右,以至于获得了“旅游模拟器”的美称。画面其实是一个比较笼统的概念,当我们在说一个游戏的画面的时候,我们一般是在说这个游戏带给我们的所有视觉体验,包括光影效果、水和火的模拟、各种材质的清晰度真实度、整个画面的比例、画面的色彩等等等等,只要是我们能直观感受到的,都可以归入画面里。

    +

    从画面整体来看,奥德赛做到了时代的顶尖。优秀的光影、逼真的水波、温暖的色调、协调的比例、真实的场景,包括人物的建模等,都非常优秀。最难能可贵的是,奥德赛给我们生动地呈现出了波澜壮阔、风光迤逦、特色鲜明的古希腊景象,尽管可能不是最真实的历史,但是当我们置身其中时,仍然可以感到非常的震撼,仿佛自己就是那个时代的人,经历着相同的事。所以,奥德赛的画面不仅是技术上的时代顶尖,而且他还能带给我们一种沉浸式的体验,这对一个优秀的3A游戏来说,是必不可少的。

    +
    +光影效果非常不错 + +
    +

    当然,对奥德赛来说,他还是沿袭了“育碧特色”——各种神奇的BUG,虽然已经比起源好很多了(起源的BUG可以看这里)。比如,你会卡到一个地方无法动弹然后“失去同步”,会在地上皆若空游无所依,会有各种各种严重不严重的穿模。其中最为蛋疼的,其实不算BUG的地方就是,自动寻路。这个简直是噩梦,很多时候系统会绕很大一圈,甚至是本来已经绕了很大一圈了,结果来了个“无法跟随道路”,这是坠痛苦的。不过好在可能是阿育经历了起源雪崩式的BUG,在奥德赛里BUG已经不是一个很严重的问题了,至少可以完成任务不是。

    +
    + + +
    +

    人物:形象丰满,行为骨感

    +

    谈到剧情就不得不稍微说一下人物了。奥德赛对人物的刻画是非常到位的,作为一部史诗大作,奥德赛不仅对主角马拉卡和一些主要人物进行了细致入微的刻画,并且对一些关键配角也有非常到位的描写!

    +

    首先说一下主要人物:马拉卡、妈和妹妹。马拉卡无疑是最核心的人物了,玩家在关键剧情上的不同选择会呈现出多面的马拉卡形象,由于我选择的是真情圣母路线,所以就按照这个形象说。马拉卡给人的最大印象,在上面已经说了,作为一个铁血真汉子、无情雇佣兵,他的内心其实是非常渴望亲情的,无论是一直心心念念想要找到妈,想要挽回失足妹妹,还是对Phoibe如待女儿一般的呵护与关爱,抑或是对Markos的帮助,都体现了一个流浪在外无家可归的人向往亲情、渴望家庭的内心最真切的期盼。马拉卡不滥杀无辜,同情战争下家破人亡的底层百姓,路见不平也能拔刀相助,时而放荡不羁,时而收敛拘束,就好似武侠小说里的绝世大侠,执剑走江湖,四海皆为家。在游戏的时候,左下角的提示会出现“xx号是你的家,记得常回来看看”(大意如此),不禁让人唏嘘,马拉卡总是在外漂泊,以船为家,在注定不平凡的寻亲之路上经历这么多人生的坎坷,着实让人心疼!另外,马拉卡这个角色还透露着呆萌的气质,一方面是来自配音+动补演员本身的气质,另一方面就是在游戏中,马拉卡的确也很幽默,经常开一些黄腔,引发一些笑话。这些对丰富和塑造人物形象都非常有帮助。

    +
    +性感吗?我可以 + +
    +

    然后再说说一些关键配角,这里说几个人:妈、后爸、Phoibe、基友Brasidas、炮王阿尔西比亚迪斯、苏格拉底。这几个人应该是除了主角之外给我留下印象最深刻的几个人了。

    +

    奥德赛对妈的刻画是非常到位的:坚强独立、能力突出,同时也和马拉卡一样渴望完整幸福的家庭。在找妈的路上,我们已经通过几个支线知道了妈是如何去找妹妹,如何带着她逃跑,又是如何在失去她之后绝望,到她最后重新振作成为一岛的领袖的。在这个过程中,妈的形象跃然纸上,后来和马拉卡一起挽救妹妹更是突出了她追寻亲情的一面。

    +

    对后爸的笔墨虽然不多,但是也能大致勾勒出一个教子严厉但关怀备至、有些教条但也饱含感情的一个将军形象。作为一个将军,他当初在悬崖边没有办法与神教抗衡,但是他也在一直懊悔。在几个CG里,我们都看出来,尽管他是继父,但是他仍然对马拉卡和妹妹视如己出,教导和关爱也都无微不至。

    +

    Phoibe和Brasidas应该是马拉卡人生中非常关键的两个人了。一个在他的生活中和他超越了朋友的关系,一个在他的斗争中给予了莫大的帮助。可以说,Phoibe与Brasidas是马拉卡少年时和成年时的两个缩影,一个象征着无邪的天真烂漫,一个象征着成熟的稳重冷静。站在马拉卡的视角,他想要紧紧地把握住他们,不但是对朋友的珍惜,更是对自己这两面的珍视,在他的心里,永远有一个小孩子,愿意陪着Phoibe一起长大,也永远有一个大人,能够和Brasidas并肩作战。

    +
    + +
    马拉卡对Phoibe非常关心,因为她是最亲密的人
    +
    +

    其实说实话,我对Phoibe是又爱又恨的,爱的是她的善良机智,恨的是她的自作聪明,无视马拉卡警告对自己的能力没有清晰的判断,导致自己身死他手,实在让人惋惜。显然,既然作为一个玩家,我已经能够对她产生这样的情感了,那么无疑,奥德赛对Phoibe的塑造也是相当成功的,虽然不那么讨喜,但是至少她是真实的。

    +

    基友的镜头没有Phoibe那么多,但是当他在贩夫仓库第一次出场时,就注定他有一个悲剧式英雄的解决了。在熊熊燃烧的大火中单挑对方几个好汉,与马拉卡多次在战场上默契配合,在斯巴达时多次帮助主角一家恢复名誉,以至于最后也是战死在战场上的,这样一个人物,实在令人肃然起敬。而且,基友不但是一个优秀的将军战士,他其实也很善良,他在能不杀人的时候是尽量不杀人的,从贩夫,到后来的斯巴达剧情,乃至到DLC的冥界,都体现了基友刚毅且善良的一面。然而,在DLC冥界那里,只要选错一个选项Brasidas就会留在冥界,这个行为这个剧情无非就是编剧强行喂屎表达泛滥圣母心的一面,实在是为这个角色抹黑!

    +
    +基友真的太帅了 + +
    +

    最后,炮王阿尔西比亚迪斯和苏格拉底的刻画也是相当有意思。在游戏后半段这两个人都会有很多任务,炮王会不断地让你去帮他约炮,或者和他约炮,而苏格拉底就是不断和你强行抬杠探讨哲学,总的来说,这两个人物的刻画也非常鲜明。其他的包括船长、鬼魅、斯巴达领袖、表弟,甚至是那个性欲很强的老女人,奥德赛对他们的刻画也都有所突出,所谓众生各相,让玩家在走剧情、赏风景的同时还是感受古希腊多姿多彩的人文特色。

    +

    但是,正如这一节的标题所说的,很多人物的形象是很丰满,但是在一些细节方面,人物的行为动机是严重匮乏的,以至于玩家会觉得“为什么非要这么做”,这样的设计对人物的刻画是有害的。举个典型的例子,亲爸毕达哥拉斯,莫名其妙找到一个神器,然后莫名其妙又觉得自己无敌了,最后又莫名其妙地挂了,过完剧情玩家只会觉得这个人脑子有点不正常,至于其他的性格、形象、心理活动?不不不,脑子有问题就是了。人物行为骨感集中体现在DLC1,这也是DLC1被大家广为诟病之所在。上面已经说过这个问题了,这里不再赘述。

    +

    最后的最后,在游戏结局时马拉卡与苏格拉底在墓园的对话让人感慨万千,在此摘录如下:

    -

    设序列且级数收敛,则序列收敛的充分必要条件是:

    +

    Alexios:Phoibe,她从没有过小女孩的时光,我总是以成人的方式与她对话。 +苏格拉底:这是因为你尊重她。你可以问问自己为何这么选择,Alexios,但千万不要质疑自己的决定。 +Alexios:谢谢你给了她该有的葬礼。 +苏格拉底:在极乐世界中还有无数的人,这些受到祝福的人已经获得了永恒的喜乐,其中一个就是我们伟大的伯里克利。 +Alexios:基于他的一切作为,全雅典都欠他一份情。 +苏格拉底:我们会称他为“雅典第一公民”不是没有道理的。他是个有很多贡献的人,但也是个孤僻的人。 +Alexios:要是他肯让我们分担他的重担就好了…… +苏格拉底:我们只能从他表现出来的部分来了解他,但谁敢说那就不是他真正的自我呢? +Alexios:这场战争牺牲太多人了,就连布拉西达斯都陨落了。 +苏格拉底:或许如此。但身为一个斯巴达人,他已经尽了自己的义务并战死沙场了。 +Alexios:我当时也有尽全力帮助他。他是我的朋友。 +苏格拉底:你还活着就是他最好的复仇,人们会将他作为英雄来尊敬的。 +Alexios:人们会记得我们是如何打垮克勒翁的。

    -

    这个定理的证明之后有空了补充。

    -

    后记

    -

    实际上无穷根式分为右无穷根式(Right Infinite -Radicals)和左无穷根式(Left Infinite -Radicals)两种,这里重点讨论的是右无穷根式的情况,左无穷根式可以仿照进行推导。

    +

    类型:转型成功,平衡欠佳

    +

    起源之前的刺客信条属于传统意义上的ACT游戏,从起源开始,逐渐向RPG转型,到了奥德赛,已经形成了ARPG的初步格局。尽管现代游戏ACT、RPG、ARPG的界限已经趋于模糊化,但是我认为我们还是有必要对游戏进行一个粗略的分类,以更好地对游戏整体的类型风格进行评价。

    +

    如果用ACT的视角审视奥德赛,那么奥德赛显然是不合格的。这体现在以下几点:

    +
      +
    • 正如剧情一节所述,奥德赛非常侧重剧情的讲述,大量的CG、对话、支线任务就是为了铺垫剧情、描绘人物形象,这和传统的ACT游戏淡化剧情是相悖的;
    • +
    • 奥德赛的养成元素非常多,这体现在等级压制、等级上限、装备体系、技能体系上,尽管游戏默认的是等级跟随,但是等级提高带来的技能点收益是非常重要的,尤其在打传奇动物和传奇生物的时候更为显著;
    • +
    • 奥德赛的装备体系决定了它不是一个纯粹的ACT游戏,游戏的蓝装、紫装、黄装品类非常多,并且,极品装备是满属性的紫装,这就意味着玩家可以追求极致一刀99999,尽管奥德赛没有刻意去引导玩家这么做,但是显然,很多玩家已经开始爆肝了。
    • +
    +

    但是另一方面,我们也不能纯粹从RPG的视角去看待奥德赛,因为游戏尽管整体上非常看重技能,但是如果你没有极品装备而只有黄色套装,那在很多时候,你还是要通过射箭、刺杀或开启无双模型与敌人硬刚,这对你的手法还是有所要求,比如闪避、防反、换装、流派,如果你不想“失去同步”,在高难度下还是需要一定的熟练程度才能手起刀落斩敌人于马下。尽管和起源的防反、马战比起来这都是小儿科,但手残玩家挑战奥德赛的噩梦难度也还是颇有难度的。从这个意义上讲,奥德赛还是保留了一些ACT的元素。

    +

    其实,当前纯粹的ACT游戏已经是难以走下去了,这是因为游戏作为一种艺术作品,随着时代的发展玩家越发看重它的精神内核。一个游戏可以不要高难度的战斗操作,只要它有足够优秀的剧本,那么它依然可以是神作;但是,一个游戏如果只要求高难度的操作,以及精心设计的关卡,而没有剧情或者足够有说服力的剧情,那么今天的大部分玩家是不会买账的。ACT游戏的RPG化是时代下游戏发展的一个趋势,但对刺客信条这个具有ACT传统的系列游戏来说,如何在这样的趋势下顺利转型是一大难题。

    +

    就我的体验来说,如果说起源是刺客信条转型的初步尝试,那么奥德赛就是转型的成功探索,它是一次探索,并且整体上成功了。如果你玩过起源和奥德赛,你会发现它们之间有莫名的相似性,但是给你的游戏体验又是完全不同的。上面讲到,起源的剧情是比较破碎的,而在很多地方,你不得不进行战斗,又加之战斗本身的难度,你会觉得剧情反而不那么重要,干掉敌人完成任务这个过程反而才是重点;同时,起源较少支线为主线让路的这个做法本身也是突出战斗的一大考量。如果把起源当成一个ARPG游戏,那么它“A”的成分还是更多一些。但是反观奥德赛,它的战斗就进一步简化了,没有马战,防反简单,刺客的飞雷神简直开挂,后期技能及其变态,甚至凑齐极品紫装后就是一刀一个小朋友,战斗本身就成了服务剧情的工具,玩家玩得爽就完事了。但是,仍然作为刺客信条系列游戏的奥德赛,总不能全是过剧情吧,那怎么办,疯狂增加要塞、兵营、洞穴就好了!再给你设计无穷多的支线,每个支线都让你去打怪,难度不够,数量来凑,这不动作的成分一下子就高上来了。

    +

    这是一个简单且聪明的想法,但对于奥德赛的定位来说,不够明智。我们可以体会到制作人想要保留APRG的动作成分和角色扮演成分,并且都要做大做强。这当然是可能的,如果做的时间足够久。但是,对于奥德赛来说却不可能,理由如下:

    +
      +
    • 开发时间短。尽管实际的开发时间我们不能精确得知,但是奥德赛发行于2018年10月,它的上一作发行于2017年10月,两作中间仅间隔一年,是标准的年货游戏。虽然奥德赛是魁北克工作室开发的,而起源是蒙特利尔工作室开发的,但是二者在整个系列的表现形式上具体显著的承接关系,故姑且认为奥德赛的实际开发周期约为2年,这个时间对3A游戏来说不算长;
    • +
    • 奥德赛的定位。奥德赛位于刺客信条系列从ACT向PRG转型的过渡阶段,是比起源更具标志性的一部作品。我个人认为,奥德赛的定位就是一部不成熟的ARPG过渡作品;
    • +
    • 经验的缺乏。显然,阿育对如何结合刺客信条系列传统的ACT元素与RPG元素的经验还有所缺乏,在短时间内要做一部顶级APRG游戏是非常困难的。记得是谁说过,“奥德赛就是想让你玩两年,直到下一部作品问世”,于是,他们把奥德赛的内容强行用问号、感叹号去填充,让玩家去爆肝刷装备,然后体验一刀99999的快感,但实际上,这种大而空的填鸭式内容对大部分骨灰级单机游戏玩家都没有吸引力,有的只是机械重复式的屠杀敌人,毫无乐趣。
    • +
    +

    所以说,在奥德赛的游戏类型上(ARPG),平衡性是不够优秀的。大到动作成分与角色扮演成分的平衡,小到装备的平衡、技能的平衡、数值的平衡等多方面,奥德赛的“全都要”战略俨然对大部分玩家来说只是一个噱头。如果只把奥德赛玩成体验剧情、模拟旅游,那奥德赛是再好不过的,可是一旦要玩操作、玩养成,奥德赛好像就走向了低配版的国产网游之路。游戏类型的平衡,是奥德赛的一大不足。

    +

    总结:育碧特色,系列之巅

    +

    总的来说,和以往的刺客信条年货作品不同,奥德赛算得上是一部精心打磨的作品,无论是剧情、画面,还是人物、音乐,抑或是育碧特色BUG,都看得出来是经过仔细调教了的。我个人认为奥德赛是综合看来刺客信条系列的巅峰之作,IGN评分9.2也算是比较中肯的评价了(对比起源9.0和巫师三9.3的评分)。如果要我来打分的话,我会打一个9.0分,要是阿育能够在剧情上再多打磨打磨,就可以给到9.5分。如果要说和巫师三差在哪儿,就差在剧情上。

    +

    另外,在本文中我没有谈到奥德赛的音乐,但这并不意味着它不出色,相反,我非常喜欢奥德赛的配乐,对比起源阴间的配乐,奥德赛的整体音乐风格就显得非常阳光和带感。OST可以在B站或者QQ音乐搜到,我已经循环很多天了,强烈推荐大家也去听一听。

    +

    对于11.10号即将发售的英灵殿来说,我个人还是非常期待的,一是可以亲手操作猛男,二是从试玩片段来看,它革除了奥德赛中饱受诟病的一些要素(比如剧情、满屏幕的问号、又臭又多的支线,过于强调等级和装备等),同时还保留了优良的画面。从这两点来看,英灵殿无疑是让人期待的。不管你预卜预购,我反正是已经预购了(还是等打骨折更划算一些/(ㄒoㄒ)/~~)

    ]]>
    - 数学 - 数学分析 + 游戏 - 玩后感 - 数学 随笔 + 游戏 + 生活 + 刺客信条
    - 巫师三:一个告诉你原来游戏可以是艺术品的神作 - /2020/10/09/19/34/ - 巫师三是一款已经被时间证明的,当之无愧的神作。巫师三没有波澜壮阔气势浩天的景致,却有风格迥异人文色彩浓厚的地区特色。威伦的破败荒凉贫穷凄苦,为了一双鞋可以连父母都能出卖;诺威格瑞的大城风范,有日进斗金的土豪乡绅,也有困守城中的穷困百姓,女巫猎人穿梭其中,种族矛盾暗流涌动;史凯利杰群岛的剽悍民风,对外来者的敌视,对入侵者的抵御,捍卫土地的决心让诸多岛屿团结在一起;凯尔莫罕的静谧安详,森林环抱狼堡,猎魔人镇守故乡,仿佛出世的仙境,危机四伏但又简单质朴;陶森特的梦幻美丽,人们自得其乐,生活在这宛若仙境的城都中。不仅仅是景色,巫师三更是以它的剧情和对人物的刻画出名,抛开难以入流的战斗系统,单凭巫师三对剧情的把控、对人物的描写和对画面的雕琢,就足以令这部游戏称为艺术品。在经历了完整的主线和DLC流程后,几乎没有玩家会给巫师三一个低分,尽管它有瑕疵,但是它在剧情、人物和画面上已经到达巅峰造极的水平了,更难能可贵的是,这三者完美融合,在体验游戏的同时,给予玩家美的享受、身心的震撼。这是奥德赛无法比拟的,也是大多数游戏难以企及的高度。

    + 《Assassain's Creed Valhalla》玩后感 + /2020/11/12/23/23/ + 刺客信条:英灵殿(Assassin's Creed +Valhalla,下称英灵殿)无疑是刺客信条“神话三部曲”中最成功的一部,无论是从游戏的质量上,还是从销售成绩上。在80个小时的主线与10多个小时的“内置DLC”游玩后,我个人对英灵殿的评价可以总结为“自我突破,承前启后”。说它自我突破是因为它革除了起源和奥德赛中的绝大部分缺点,在自我的改进中完成了里程碑式的跨越;说它承前启后式因为它标志着刺客信条系列一次华丽而成功的转型与过渡,是游戏特色与市场充分交合后的最佳发展方向,是游戏缺点自我祓除与优点渐进式凸显的结果。本文将重点从游戏的任务系统、剧情节奏和人物刻画这三个方面介绍英灵殿的蜕变,至于游戏画面和风景,还有育碧特色BUG,都是刺客信条系列经典的历史特征了,在本文不再赘述。

    -

    前言

    -

    由于巫师三的世界观过于宏大,一篇未经雕琢的读后感不能道尽巫师三的所有优点,故本文旨在从宏观上呈现巫师三是如何完美统一任务、节奏与剧情这三者的。至于其他的,我们有机会留在以后再说。我相信,通关巫师三的玩家,一定会承认巫师三在任务的组织和剧情的展开上非常连贯丝滑,而且剧情上的节奏递进、叙述上的跌宕起伏都让人回味无穷。游戏本质是在讲故事,但是巫师三成功地给予了玩家沉浸式的体验,也就是,我们从观众的角色深入到了主角的角色,从游戏外的世界走进了游戏内的世界,仿佛每个人都是真实可及的,都是有血有肉的,都是有情有义的。那么,如何讲好这个故事,把观众代入到游戏中,就是巫师三不同于其他游戏的关键所在,这也是这篇玩后感的主要内容。

    -
    -被附身的Geralt帅中泄露出一丝痞气 - -
    -

    主线为林,支线为溪

    -

    巫师三的一大特色就是它的任务系统非常丰富,从表层看,巫师三的主线只有一个——寻找女儿,但是,在每个地区,都会有非常多的支线为主线提供背景。就好比一个建筑,主线是骨架,支撑起整个游戏的大致轮廓,而支线是钢筋水泥,垒起了整个建筑的最终格局。当然玩家可以只做主线不做支线,但我相信如果不做支线,这个游戏的乐趣就会少一半。

    -
    -父女之间的亲昵 - -
    -

    说到支线繁多,一个典型的对比就是刺客信条奥德赛。同样有非常多的支线,但是从我个人的游玩体验来说,我宁愿无视奥德赛的90%的支线,直接一条路走完主线,但是巫师三的支线不然,玩家会有强烈的意愿去做大部分支线,在做支线的过程中解锁问号,发掘游戏中隐藏的剧情,是巫师三最为亮眼的一大特色。如果要对比巫师三和奥德赛的任务系统,可以从下面几个方面评价:

    +

    任务系统:众星捧月映空青,稀光散落满夜明

    +

    剧情节奏:一马平川驰此间,峰峦叠嶂牛羊见

    +

    人物刻画:穷极千里观百态,瓦泥滂沱照众生。

    +

    总结:长路漫漫终将至,共邀朝阳行远方。

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +]]>
    + + 游戏 - 玩后感 + + + 随笔 + 游戏 + 生活 + 刺客信条 + +
    + + 《电脑游戏:文本、叙事与游戏》纪要 + /2021/08/14/18/08/ + 本文是《电脑游戏:文本、叙事与游戏》的简单纪要,在此备份。

    + +

    游戏与叙事

    +

    叙事理论

    +
      +
    • 每一个叙事都有两个部分:一个是故事,指事件的内容或串联(动作、发生的事),再加上所谓的存在物(角色、场景中的事物等);二是话语,即内容赖以传达的方法。
    • +
    • 故事的事件在时间和空间上都是按照一定的顺序排列的,这就是所谓的情节设计
    • +
    • 叙事事实的三个方面:故事(内容)、叙事(话语)、叙述。 +
        +
      • 童话《灰姑娘》中包含了一系列故事的事件,可以通过很多种不同的方式被呈现出来。
      • +
      • 叙事的关键要素:对事件加以安排,使之在话语当中凸显出来,对它们的时间长度、发生频率和次序予以控制。
      • +
    • +
    • 隐含作者与隐含读者。
    • +
    +

    叙事理论与《博德之门》

    +
      +
    • 游戏包含了一系列由玩家所触发的新事件,允许玩家实时行动;也包含了过去的事件,已经被安排好的。
    • +
    • 事件被提前预制好,玩家是做出情节安排的人。
    • +
    • 同步叙事:向玩家传达屏幕上的行动、事件、得分和对话。
    • +
    +

    叙事的限度

    +
      +
    • 叙事理论追问的是:故事情节是从何种角度被描述的,我们是通过谁的眼睛了解叙事当中的事件的,又是谁的声音在传递信息,是谁在策动事件,谁来决定事件的持续时间、发生频率和次序。
    • +
    • 《博德之门》以一种玩家无法操纵的形式讲述事件,以过场动画、NPC的对白和角色简介的方式被编织在一起,并与玩家发生联系。
    • +
    • 已然事件实时事件
    • +
    +

    游戏与快感

    +
      +
    • 电脑游戏究竟要驱使玩家去做什么。
    • +
    +

    角色的生成

    +
      +
    • 选择外观、种族、职业、阵营、声音、名字。通过这些选择,让玩家了解游戏玩法与背景,同时限定了一系列数值。
    • +
    +

    角色、特征和游戏

    +
      +
    • 角色既包含了审美层面和故事层面的偏好,也涉及战略方面的考虑。人物的特征会影响(约束)其行为。
    • +
    • 但是玩家的风格和喜好决定了角色内在的可能性以怎样的形式表达出来,或怎样被压抑。【如果想要增强角色自身的特征,应该限制玩家的可操作性,如从数值、剧情、环境、探索度等层面。】
    • +
    • 在电脑游戏中,玩家的input和游戏所提供的特征描述之间的互动可能引发不可预测的行为,与某个角色表面的扁平性发生矛盾。因此,仅仅按照设定的属性和特征去描述一个游戏化身或角色是错误的。【可以通过“如果不这么做,游戏就难以进行下去”的思路引导玩家进行特定的操作。】
    • +
    +

    沉浸、卷入与“心流”体验

    +
      +
    • 玩家将游戏系统看做一个整体,但是在这一游戏系统中,玩家还必须关注一系列次级系统,那就是角色的行动、武器、物品和技能。
    • +
    • 在文学研究中,沉浸感是指读者所享受到的放弃批判思考的全神贯注。认知心理学的图式理论使我们能够感知周遭的事物并进行有效的处理,这一能力有赖于此前通过阅读、个人经验和别人的建议而建立起来的对类似事件的已有知识储备。【也就是说,沉浸感实际上是一种记忆唤醒机制?】
    • +
    • 只要故事、背景和界面保持统一的图式,玩家的审美体验就能在很大程度上保持沉浸感。
    • +
    • 卷入,是一种经过更多思考的批判式的参与方式。 +
        +
      • 当读者面临不熟悉的题材,或者文本很难左右其反应时,他们被驱使着一再重读或反复斟酌其中的信息。
      • +
    • +
    • 沉浸的快感来源于我们伴随某种熟悉图式的退潮和涌动而完全专注其中。卷入的快感则来自我们从文本之外的视点去识别一件作品如何将互相矛盾的图式翻转、连接。【“相互矛盾”可以是新的情节、新的人物、新的动作,总之与既有的图式产生了区别之处。】 +
        +
      • 当屏幕上的场景要求即时的、固定的反应时——比如思考、凝视、阅读、击打、探索、行走——就促使玩家进入沉浸状态。
      • +
      • 当玩家不得不与屏幕上的行为逐渐拉开距离时,就进入卷入的状态。可能是由于角色的特性、一条迷失的小路、一个强大的敌人等。这时候就要寻求解决之道:重复行为——>重新经历——>查找攻略。
      • +
      • 沉浸和卷入相互依赖,玩家在流动性和安逸心情之间切换,游戏世界由此塑造体验。【战神的战斗、探索、叙事是一种沉浸,而其中的解密、情节、道具、新的敌人又是卷入。不断创造卷入,然后将卷入转化为沉浸,再创造新的卷入。
      • +
    • +
    • 随着游戏难度的提高,玩家的应对能力也在进步,游戏需要与玩家的水准相匹配。这时候玩家总说自己“处于巅峰状态”。【战神。】
    • +
    +

    空间、导航与情绪反应

    +

    《异域镇魂曲》与《寂静岭》

    +
      +
    • 《寂静岭》的音效使之恐怖感上升。
    • +
    • 《寂静岭》的线性结构能让游戏始终保持一定的节奏和紧张感
    • +
    +

    导航:迷阵、根茎与迷宫

    +
      +
    • 空间导航有两种模式:迷阵与根茎。 +
        +
      • 迷阵意味着在向唯一的出口有条件地前进,缺点是玩家只能被引导到预先设定的唯一出口。
      • +
      • 根茎并没有特定的方向比其他方向更有利,缺点是结构的缺失。
      • +
      • 游戏当中的迷宫应当处于二者之间:要实现的目标应足够强大,引导玩家前进;同时设定开放式结局,令玩家可自由探索。【巫师三和BOTW的一大共性(实际上Sekiro也是):主线分支化,鼓励玩家自主选择、探索。核心要点是:自由地引导。】
      • +
    • +
    • 恐怖效果地生成总是基于“一个简单而明显地基本公式:怪物使常态遭到威胁。” +
        +
      • 《寂静岭》中,常态被怪异之物威胁并摧毁。
      • +
      • 《异域镇魂曲》中,怪物本身就是常态。
      • +
    • +
    +

    游戏文本与遍历函数

    +
      +
    • 呈现于受众面前的称为“脚本单元”:是不同玩家将游戏中的信息“玩出来”的。
    • +
    • 存在于文本之中的称为“文本单元”:是游戏提供的所有潜在信息。
    • +
    • 使脚本单元自文本单元中显露或生成并呈现于玩家面前的机制,叫做“遍历函数”。
    • +
    • 遍历函数的几种变体:活跃性、可确定性、可访性、连接性。 +
        +
      • 活跃性(与文本单元和脚本单元的数量与稳定性有关):《异域镇魂曲》比《寂静岭》更加活跃少停滞,因为前者任务更多,且一部分是强制性的,而且文本单元更丰富,玩家选择和组建的脚本单元的可能性也都更多。
      • +
      • 可确定性(给定状况相同反应是否总是导致相同的结果):《异域镇魂曲》比《寂静岭》更加不确定。
      • +
      • 可访性(玩家是否能在游戏中任意一点进入游戏文本):《异域镇魂曲》比《寂静岭》可访性更强。
      • +
      • 连接性(文本单元间时空的连接):《异域镇魂曲》比《寂静岭》连接性更强。
      • +
    • +
    • 《寂静岭》更加稳定、有节制、更具确定性的特点塑造了它迷阵般的结构。《异域镇魂曲》较富于开放性的遍历模式则表现出如根茎般的扩张趋势。
    • +
    +

    替身

    +
      +
    • 沉浸分为感知层面的沉浸和心理层面的沉浸。 +
        +
      • 感知层面:游戏体验垄断了玩家的感觉,如听觉、视觉。
      • +
      • 心理层面:玩家全神贯注地投入到想象中的游戏世界去。
      • +
    • +
    • 《异域镇魂曲》的等距视角和游戏主角本质上的复合性在感知和行为两方面都大大消解了玩家与化身的同一感(削弱了感知层面的沉浸)。但游戏以另一种方式令玩家沉浸其中:大量描述性文本的阅读,以及关于玩家对游戏主角、其作战团队及其物品的巧妙运用的细节信息的缓慢积累。【古剑三】
    • +
    • 《寂静岭》是感知层面的沉浸,各种即时状态。
    • +
    • 《异域镇魂曲》拒绝被匆忙对待,而《寂静岭》催促着玩家从一个地方到另一个地方,抵达终点。
    • +
    +

    (更多细节,侧重代入感,带来感知沉浸)小<---------视距--------->大(更多信息,侧重策略性)

    +

    角色扮演

    +

    社会符号学视角

    +
      +
    • 图像语法:再现性(再现这个世界)、互动性(允许文本的作者和读者之间的沟通,以及文本中虚构人物与读者之间的沟通)、组织性(使文本各要素连贯一致,由此组合起来以进一步实现它们承载的含义)。
    • +
    • 多模态理论:文本如何将不同的符号模态——如讲话、书写、声音和图像——组合在一起,游戏是多模态的文本,因此应该思考,动画、视觉设计、音乐、文字文本和声效等多种沟通模态的联合是如何实现前述三个主要功能。
    • +
    +

    克劳德——大英雄

    +
      +
    • 克劳德这样的角色在相当大的程度上利用了民间文化、口头叙事和罗曼司等传统形式,通过精心构建的寓言故事,为日常生活中的过渡仪式和苦难提供了情感慰藉、道德争论和心灵拷问。
    • +
    • 电脑游戏是被翁称为高科技社会“次级口述”的案例之一,它是口述文化这种思维模式的进化,基础是文学和以技术为媒介的文化。
    • +
    +

    克劳德——数码傀儡?

    +
      +
    • 将克劳德这样的文本建构视为一种固定的对象却可能导致我们忽视玩家—化身这一重联系,甚至是忽略普遍意义上的文本的含义。
    • +
    • 玩家同时也是一种文化资源,是阐释者,也是粉丝艺术和粉丝写作当中对游戏资源进行改编的人。
    • +
    +

    化身的操纵

    +
      +
    • 玩家既是化身,又不是化身,这是游戏体验的核心。
    • +
    • 文化研究强调能动性(agency)这一概念,这是读者主动阅读时所具备的积极素质,因此我们可以认为对化身行动的操纵程度可以等同于更为广泛意义上的掌握文化权力的程度。 +
        +
      • 我们是自主有力的社会行为者:可以选择为玩家可以参与文本的程度达到前所未有的地步而欢呼。
      • +
      • 我们仅仅是他者的再现:玩家只能接受并操纵由公司设定的游戏文本决定提供给他们的角色。
      • +
    • +
    • 随着游戏在不同时刻在给予性结构或需求性结构上的转变,玩家的参与方式也将发生变化。战斗可能是最为偏向需求性结构的情况,此时游戏系统似乎称为游戏的全部。
    • +
    +

    多模态的给予与需求

    +
      +
    • 文本产生的需求由文本当中的不同模态通过相互组合以不同方式实现。 +
        +
      • 如音乐节奏的变化暗示着游戏文本变化。
      • +
      • 如图像的变换预示着危险的迫近。
      • +
      • 此时玩家具有双重身份:一方面,玩家与化身融为一体,二者皆为行动者;另一方面,玩家又像是一个操纵木偶的人,拉着化身的线,甚至是某种类型的作者,作为建立在规则之上的因果结构的一部分,以一种受限语言进行书写。
      • +
    • +
    • 镜头元素同样有助于文本互动。 +
        +
      • 置于较低处,好像和角色并肩作战,去除了需求性结构所产生的木偶操纵般的感觉。如果我们被赋予了操纵能力,同时视角又较高,则操纵的感觉会相当强烈。在需求性结构处于最紧张的时刻,将镜头拉低和拉近能够使玩家不失时机地与角色接近。【战神4】
      • +
      • 镜头通常置于角色上方固定的位置,以提供操纵感,让玩家像对待、培养宠物一样对待化身,提供了给予性结构,并提供一定的稳定性。
      • +
    • +
    • 对游戏世界的探索感得自一种更弱的需求形式——诱导而非命令。【只能在有强烈需求的时候才能给予玩家强烈的暗示,或命令;而在一般情况下,需求更多给予微弱的引导。这一方面战神做得很好。】
    • +
    • 【游戏需要不断地、交叉地提供需求性结构和给予性结构。】
    • +
    +

    文本再创作:网络上的粉丝文化

    +
      +
    • 在游戏的需求性结构(搅动因果链条,推动你跨越失事列车或迷宫实验室的解密关卡,使你陷入紧张情绪之中,让你迷路和让你在面临众多敌人的时候产生焦虑感)和给予性结构(安排前后语境、风景、背景故事、动机和心理,将读者-受众拉入文本的移情网络和想象性延展空间中,和传统叙事中的运作方式一样)之间存在一种辩证关系,正是二者的结合提供了快感,而在以游戏为核心的粉丝创作中,二者将以相当不同的方式结合在一起。
    • +
    • 粉丝是如何使用游戏系统的?游戏攻略、创意性写作、漫画。
    • +
    +

    游戏攻略作者

    +
      +
    • 这是一种探索游戏系统本身的快感。
    • +
    • 他们用祈使句(第二人称)控制彼此的游戏行为——这是游戏攻略的特征。
    • +
    +

    虚构文学写作者

    +
      +
    • 过场动画属于给予性结构——它们只作叙事性陈述,并无互动性。过场动画可能表现出作为核心的游戏和外层叙事之间的分离。
    • +
    +

    诗人

    +
      +
    • 直接阐释了游戏主角的情感,这是游戏文本无法做到的。【现代游戏在有面捕和动捕之后是可以实现的。】
    • +
    +

    “三无”漫画家

    +

    结论

    +
      +
    • 在围绕《FF7》的粉丝创作当中,再现系统和游戏系统是彼此分离的。
    • +
    • 以再现系统复制对玩家与化身的第一人称认同是可能的,但更为常见的还是借助游戏再现系统所提供的可能性,生产出第三人称的视角、叙事和形象创造,并为了自己获得快乐而对其进行挑选、放大或转换。【采用第一人称就限制了创作空间,而第三人称视角提供了无限大的创作空间,不仅仅限于游戏文本所提供的现有信息。】
    • +
    +

    动机与网络游戏

    +

    再现动机:欢迎来到鲁比卡世界

    +
      +
    • 《混乱在线》以科幻小说为基础进行设定,在游戏网站上,鲁比卡世界的故事片段以各种形式呈现。
    • +
    • 我们借以创建化身的个人标准,远比我们最初所能意识到的更能暴露自身经历及偏好。
    • +
    +

    娱乐动机:游戏、目标与策略

    +
      +
    • 娱乐动机相关的要素:战略、目标、即时事件、机遇、规则、技能、探险、升级、数值。
    • +
    • 娱乐动机更强调玩家所操纵的游戏中的那个化身作为游戏的一个元素存在,是一个具有战略价值的符号单元,而非一个角色。
    • +
    +

    公共动机:共享鲁比卡世界

      -
    • 支线多样化:巫师三的支线是多样的,支线之间没有重复,猎魔人委托可能会更无聊一点,但生意人的事情嘛,不寒碜。除了猎魔人委托之外,寻宝任务有解密探险要素,区域日常支线有搞笑奇葩要素,重要支线甚至会改变整个巫师三世界格局。然而,奥德赛的支线是单调的,支线的选择不会影响主线的进程,支线品类非常单一,并且缺乏逻辑。巫师三支线给玩家的动力是充分的:委托赚钱,寻宝得装备,日常支线放松一下,重要支线扣人心弦;而奥德赛不是的,除了几个从头到尾都有的支线之外(类似于巫师三的寻宝任务),其他的支线毫无意义,既对主线没有补益,而且也缺乏特色。
    • -
    • 支线重要化:当然,支线多样丰富是一方面,更重要的是,支线是需要起到作用的,有时候甚至是决定性的作用。巫师三的主线和支线的分工相当明确:主线用来推进整个游戏进程,而支线则负责主线缺失的娱乐性与严肃性。这大大补充了单主线的贫乏,让玩家在游戏的时候不会觉得推进主线是无聊的,因为在同时还有大量丰富有趣的支线在支撑游戏的饱满度。而且,巫师三的支线并不是其他游戏中如过往云烟般的支线,而是不同的支线会在主线的不同时间点产生决定性的影响。比如,迪胖的支线会决定整个游戏最终的政治格局,这是从大了讲。从小了讲,如果细心留意,就会发现很多很多支线都涉及到游戏中不同人物的命运,即使是路人,我们也能够感到他们是真实的,而不仅仅是一堆数据。这些都是支线带来的重要积极影响。巫师三赋予了支线游戏的精神内核,并令其成为承载游戏厚度的“实力担当”。
    • +
    • 公共动机包括一个游戏和其他游戏之间的关系、玩家由游戏之外带入游戏的期待,以及游戏本身的共享属性。
    -
    -这样有趣的小支线在巫师三里不胜枚举 - -
    +

    结论

      -
    • 支线惊奇化。巫师三的支线时常给人一种惊奇的感觉,无论是之前无意之中做的支线会在后来的某一天发现原来会产生这样的影响,还是后来做到的支线会发现是之前剧情的关键结果,这种突如其来的“因果”呈现都带给玩家游戏体验上的冲击。重点是,正所谓大音希声,这样的惊奇化支线完全不需要漫长的铺垫,而只需要一个小小的“接口”,把因果串联起来就可以达到这样震撼的效果。一个典型的例子就是在DLC石之心里,拍卖行里的一个小支线展现了维瑟米尔的不为人知的一面,不但极大丰富了人物形象,而且还带给玩家无尽的唏嘘。此类四两拨千斤的支线在巫师三里比比皆是,
    • +
    • 游戏动机的三种类型:再现动机(戏剧性的、表演性的、修饰性的、图像化的、叙事的)、娱乐动机(指向游戏层面的:数值、升级等)和公共动机(共有的期待、广泛的网络文化、游戏所分享的外部世界)。
    -
    -一个非常小的细节,但是却折射出了两个饱满的人物形象 - -
    -
    -一个非常小的细节,但是却折射出了两个饱满的人物形象 - -
    -

    所以,巫师三成功的一大关键因素就在于他巧思的任务系统,尤以支线为代表,主线与支线的交织,主线推动整个剧情的发展。支线却承载了多样化、重要化和惊奇化的具体内容,让人家流连忘返,深陷其中。

    -

    选择困境,人生模拟

    -

    当然,除了巫师三之外,还有很多游戏的支线也很丰富,主线支线的组织也很巧妙,那凭什么巫师三这么牛逼呢?这就是巫师三的第二个牛逼之处:通过合逻辑的选择带给你极尽真实的人生体验。

    -

    巫师三的任务绝大多数都是需要玩家做出选择的,无论主线还是支线,这在从威伦到诺威格瑞之后尤为突出。不同于很多游戏可有可无的“可选项”,巫师三的每一个选项都是经过反复斟酌的,不存在无意义的选项。威伦雷索的选择直接决定了他之后会不会在凯尔莫罕的时候来帮你;凯拉的连续选项直接决定了她的命运,也决定了兰伯特的命运(这个有些小伙伴可能还不知道);威伦有个狼人的支线直接决定了这个悲惨爱情故事的结局;诺威格瑞迪胖的支线决定了游戏最终的政治格局。对主线来说,这样的决定性选项无处不在:血腥男爵的选项,特莉丝和椰奶的选项,Ciri的一系列选项等等。在巫师三中,选项的重要性直接以前后的因果关系体现出来,也许你的不经意的一个选择,就很有可能在未来决定了不同的世界线,其中的很多选项,你甚至都意识不到是由自己的决定造成的,这是不是有《命运石之门》的感觉了。

    -
    -Ciri猎魔人结局片段 - -
    -
    -Ciri猎魔人结局 - -
    -

    我们之前说了,巫师三带给玩家的是沉浸式的体验,那实现这一点的关键就在“真实”。这里的真实,大部分是成年人的部分:我们会遇到各种各样的决策,尽管有句话是说“小孩子才做选择,成年人全都要”,但实际上,只有小孩子才能全都要,成年人的世界不能兼得鱼和熊掌。有些选择比较简单,但也有很多选择,可以说是直接决定了往后人生的整个发展方向。世界上没有后悔药,所以我们常常看到成年人的无奈与悔恨。但是游戏不一样,我们可以回档,可以重来,可以把现实中无法完成的夙愿转移到游戏中,体味人生抉择的无奈,尝尽人生起伏的精彩,但最爽的,还是能够重来,再走一遭。

    -

    巫师三是在真正意义上模拟了人生: - -首先,它的选项逻辑是合理的。很多游戏,包括奥德赛在内,它们所谓给出的选项很多都是不合理的。我为什么要这么做?为什么只有这几个选择?策划是不是脑子有坑?正常人会这么做?在选择的时候,不合理、不充分的选项不但不能产生真实的沉浸感,而且还会极大破坏游戏剧情推进的合理性。巫师三不然,它给出的每一个选项都是合理的,都是玩家真的可能在现实生活中面对的两难困境,从而不会让玩家产生“这个选项好弱智”的疑惑。真实的选项才能获得真实的体验,为选择而选择的设置只能适得其反。 -- -其次,它的选项是有反馈的。所谓反馈,就是无论你选了什么选项,都能在游戏的过程中获得该选择的结果,而且在看到这个结果的时候,玩家是可以或多或少发现它的起源就是那个选项。巫师三的选项就是有始有终的,它可能在有意无意间唤起你的决定,或最近,或久远,那个时候,玩家就会惊叹:原来如此!这是很重要的,因为在现实中,人们会感到“后悔”或者“幸好”,就是这样的“记忆唤醒”在作祟。人是健忘的,但这不是说记忆会消失,而是会被暂时隐藏,所以必须要有一个触发点(Trigger)才能重新唤醒这段记忆,而记忆一旦唤醒,就会有首尾呼应的触动,进而给玩家带来震撼感。这也是巫师三能带来真实体验的一个重要因素。

    -
    -Geralt和夏妮 - -
    +

    社交性的游戏与学习

    +

    探究游戏的社交性

      -
    • 最后,它的选项是关键的。所谓关键,就是一些选项会产生极其重大的影响,此类选项在主线中最多,但也不乏一些支线。为什么说一些选项需要具备关键性?其实这就像欣赏任何一个文学作品一样,无论是小说,还是电影,剧情总是需要跌宕起伏。一平到底,或者过于波折,都会给观众造成审美疲劳。这在游戏里也是一样的。如果所有的支线都是做了就做了的那种,那么就不会给人留下深刻印象,反之,只有平淡与冲突交错的支线才会充分勾起玩家的兴趣。上面我们说了巫师三的支线都是有反馈,那么反馈对后续游戏造成的影响就可以定义为该支线的“关键度”。一个比较合理的关键度分布,应该需要满足5:3:2或者6:3:1的比例的,也就是大部分任务都是比较平淡的,立马能得到反馈的,而且对主要剧情没有影响的;少部分任务会在游戏后续呈现出比较重要的影响;而极少的任务会产生决定性的影响。举例来说,委托任务,城中任务,基本上都可以归为第一类,像凯拉、雷索这样的任务可以归为第二类,最后像迪胖、希里这样的就可以归为第三类。可以看到,巫师三的选项是非常有层次的,这样的层次感不会让玩家感到枯燥疲倦,从而更好地代入到游戏中。这也是巫师三让人停不下来的一个原因之一。
    • +
    • 游戏较之更为传统的、只能独自体验的媒介,比如书籍,可能是更适合儿童的娱乐形式。
    • +
    • 游戏文化可以被认为视为这样的一个竞技场:它创建了等级制度,并且欺凌群体当中的弱者。
    -
    -Geralt的回忆 - -
    -

    有条不紊,循序渐进

    -

    在前面的两节中,我们说了很多支线的内容,包括支线的特性、选项的特性,但是在游戏里,决定玩家游戏体验是否“丝滑”的决定因素是主线的推进节奏。巫师三的主线的推进非常顺滑,玩家不会感到节奏上的拖沓或者赶工,从百草园,到威伦,到诺威格瑞,再到Skellig、凯尔莫罕,每个地方的风土人情都在剧情中得以充分展开,每个地方,从开始到结尾,都有明显的渐变,不会造成突兀感。这就是主线承接上的无缝性。

    -

    从整体上来看,巫师三的剧情推进呈现出前期平缓,后期陡峭上升,在最后达到高潮之后迅速下降收尾的趋势,尤其是在最后经过高潮后的突然结束,让玩家回味无穷。从百草园到是Skellig是整个游戏的平缓期,在这个时期,玩家更多地是在品味一个新地点的独有风格,所以游戏没有安排太波折的剧情,只是一点一点让玩家代入到整个游戏的剧情中。之后从凯尔莫罕开始,剧情开始迅速推进,寻找Ciri——>找到Ciri——>凯尔莫罕保卫战——>决战狂猎——>真相浮出——>Ciri献身,这一系列重要剧情节点被安排地非常紧凑,这是因为经过前中期长时间的铺垫,玩家已经全然进入到角色,当前最期待的是一波快节奏的剧情轰炸。笔者在出Skellig之后,就有预感剧情一定会出现巨大转折,从乌马开始,到狂猎结束,整个线非常完整,发展也非常迅速。但在这样的快节奏下,游戏不是混乱的,而是始终围绕着一个线索在有序地推进(找Ciri——>干狂猎),这样一来,即使游戏的节奏突然加快,玩家也没有不适感。

    -
    -老爷子战逝 - -
    -
    -老爷子死去Ciri流泪 - -
    -

    巫师三的三个时期“平缓——上升——下降”是处理得很好的。平缓期大概占了整个游戏进度的50%左右,在这个期间,游戏的主要内容缓慢推进。注意,不是没有推进,而是以叙述的方式娓娓道来,逐渐营造引人入胜的氛围。上升期大概占了45%的内容,在这个阶段内,游戏的剧情迅速发展,剧情节点迅速变化,游戏状态迅速切换,然而玩家没有任何的不适应,反而更加乐在其中,停不下来。这是因为前50%的铺垫已经将玩家去完全代入到游戏中,仿佛你就是Geralt,你的使命就在眼前。在上升期的末尾,游戏戛然而止,来到下降期。巫师三的下降期非常短暂,我认为大致可以从Ciri献身开始算做下降期,在这个节点,玩家之前所有的选择都会体现在不同的结局上,突然结尾更加凸显了玩家之前决策带来的种种后果。显然,CDPR没有采取让游戏慢慢结束,而且一下子就关闭了游戏主线剧情,带来了一种“惊愕感”:“这游戏结束了!?”这样的设计是很巧妙的,但也是建立在之前的平缓期与上升期的铺垫之上的。可以说,巫师三这种有条不紊、循序渐进的三阶段推进方式,是它能够取得沉浸式体验的另一大重要原因。

    -
    -通关主线,从空荡的凯尔莫罕重新出发,寂寥涌上心头 - -
    -

    写在最后

    -

    尽管本文的目的在于从任务系统上简要感受一下巫师三为什么能带给人沉浸式的体验,但是对于巫师三来说,画面、人物、音乐等等其他方面都是促成巫师三能成为神作的重要因素,尤其是巫师三的音乐,直击人心,给人带来的沉浸感不亚于剧情,在此强烈推荐。

    -
    -Geralt太可爱了吧 - -
    -

    之后我会介绍一下英灵殿和12月份的赛博朋克2077,到时候再来看看,巫师三这款5年前的游戏是否仍有一战之力。

    -
    -陶森特美丽的风光 - -
    -]]>
    - - 游戏 - 玩后感 - - - 随笔 - 游戏 - 生活 - 巫师三 - -
    - - 《Real-Time Cameras》笔记 - /2022/05/05/21/40/ - 本文是《Real-Time Cameras: A Guide for Game Designers and -Developers》的阅读笔记,放在此备份供参阅。

    - -

    Part 1: Core Concepts

    +

    观察玩家

      -
    • 第一章:介绍了游戏应用与其中的相机系统。
    • -
    • 第二章:讲述游戏相机的基础概念与发展。
    • -
    • 第三章:介绍电影摄影的基础知识与应用。
    • +
    • 游戏过程当中与其他玩家之间的互动似乎已经成为游戏乐趣的一个重要部分。
    -

    Chapter 1: Game Overview

    -

    一个典型的游戏引擎(Game Engine)应当包括:

    +

    游戏

      -
    • 资源管理系统(Resource Management)
    • -
    • 玩家控制(Player Control)
    • -
    • 物理模拟(Physics Simulation)
    • -
    • 游戏对象逻辑(Game Object Logic)
    • -
    • 图形用户界面(Graphical User Interface)
    • -
    • 相机与视口管理(Camera and Viewport Management)
    • -
    • 网络交流(Network Communication)
    • -
    • 人工智能(AI)
    • -
    • 游戏事件管理(Game Event Management)
    • -
    • 渲染(Rendering)
    • -
    • 对象间交流(Inter-Object Communication)
      -
    • -
    • 音频(Audio)
    • -
    • 任务管理(Task Management)
    • -
    • ……
    • +
    • 《凯恩的遗产:噬魂者》。
    -
    -游戏各系统之间的联系。 - -
    -

    游戏的每个系统都有相对固定的更新顺序,如下图所示:

    -
    -游戏各系统的更新顺序。 - -
    -

    对于每个步骤的介绍如下:

    +

    玩游戏

      -
    • Input:通常是每帧更新一次,但一些游戏也会每帧更新若干次,从而精准反应玩家输入。
    • -
    • Think:大部分游戏逻辑得到更新,包括玩家输入、AI、游戏事件生成等。
    • -
    • Move:更新物理模拟、碰撞检测等内容。
    • -
    • Messaging:用于在游戏对象、游戏系统之间传递信息,比如新游戏对象的激活、开门、敌人死亡、资源加载完成、动画事件、碰撞等等。Messaging并不是一定会在Update -Loop中出现,可能会在消息发出的瞬间被处理,或者延迟处理。Messaging系统有利于不同系统之间的解耦,简化游戏录像逻辑。Messaging本质上是一种保证不同时间点游戏事件同步的技术。
    • -
    • Camera:主要包含以下步骤: +
    • 玩家与游戏机之间的联系可以在同伴的指导和建议下构成,这产生了一种有效的“中介性合作”。
    • +
    • 无论小组中的不同成员扮演什么角色,我们从男孩们的游戏失败的同步反应中,可以进一步证实合作游戏的集体性本质是显而易见的。
    • +
    +

    叙事与游戏

      -
    • 移除无用的camera
    • -
    • 激活需要的camera
    • -
    • 决定哪个camera用于渲染
    • -
    • 更新当前激活的相机
    • -
    • 更新主相机
    • -
    • 基于主相机更新玩家control reference frame
    • -
    • 构建transformation matrices用于渲染,包括特效如shaking, FOV等
    • +
    • 如何处理游戏角色的死亡对动作冒险游戏而言十分重要。《噬魂者》的处理方式是:死亡不会导致重新开始,而是进入阴间,这使得玩家在相似环境下继续游戏,且包括了许多之前不存在的道路。
    • +
    • “管控调整”语言明显见于游戏和玩家之间以及玩家和玩家之间所交换的各种问题和命令中,“高手”完全关注于游戏系统以及对操作的指导。
    • +
    • 电脑游戏的试听魅力明显降低了在游戏过程中当面讨论的需要,如果故事已经展现在你眼前,描述故事甚至事无巨细地加以讨论就显得多余。
    • +
    • “玩后叙事”可以更多地展示出游戏地叙事魅力。
    • +
    +

    结论

    +
      +
    • 作为一种指导性活动,合作性游戏比明确地尝试指导更为有效。
    • +
    • 电脑游戏嵌入了“社交外壳”,单人游戏可以转变为包含有效指导与沟通的社交活动。游戏都是那种可以形成社群和身份认同感的学习形式的绝佳例证。
    • +
    • 互动不仅指玩家和游戏之间的关系,也应包括很多玩游戏这一活动所固有的社会语境和社交关系。
    • +
    +

    游戏内外的能动性

    +
      +
    • “互动性”一次常用以解释电脑游戏的魅力。三种类型的交流模式:声明式交流、反应式交流和互动式交流。从这个角度来看动作冒险游戏,它更像式“反应式交流”而非“互动式交流”。
    • +
    +

    进入奇异世界

    +
      +
    • “奇异世界”的创造者和设计者表示,他想将生态困境注入作为整体的游戏,让玩家可以与之互动,并最终战胜困境。
    • +
    +

    对玩家能动性的研究

    +
      +
    • 论坛的集体性质还表现在以下事实中:论坛成员对论坛环境负有管理职责,他们鼓励参与者以相互支持而非破坏性的方式参与讨论。
    • +
    +

    个人能动性

    +
      +
    • 玩家可以以多种方式参与游戏,尽管游戏具有强烈的叙事目标,但它仍可以被从视觉、情感和主题等多个层次加以解读。
    • +
    +

    代理能动性

    +
      +
    • 代理能动性:人们努力以某种手段争取那些拥有资源或技能的人按照他们的意图行动,,以协助他们获得期望的结果。 +
        +
      • 游戏攻略。
      • +
      • 论坛讨论。
    • -
    • Post-Camera:更新依赖于相机的物体
    • -
    • Render:有时候一个camera view也会被用于另一个camera -view的生成过程中
    -

    对于相机而言,Occam's -Razor法则适用:最简单的相机解决方案通常是最好的。

    -

    相机系统的作用包括:

    +

    集体能动性:粉丝作品

      -
    • 管理活跃相机,保证其逻辑正常
    • -
    • 控制相机增删
    • -
    • 支持相机参数动态调整
    • -
    • 为玩家操控提供参考帧(reference frames)
    • -
    • 处理所有非交互式电影片段
    • -
    • 管理并更新视口
    • -
    • 获取frustum信息以展示view
    • -
    • 提供可选的debug能力
    • +
    • 文本在粉丝中被认为式一个可以被集体改变、改编和重写的过程,一个未竟的事业,而非静态对象。
    • +
    • 在粉丝艺术家的创作过程中,我们能够看到所有的能动性形式。在某种层次上,帖子表现出个人发挥其能动性和培养个人能力时所带有的焦虑、压力和挑战。粉丝们在对彼此的反馈、建议和专业技能表示支持和学习的过程中,利用了代理能动性。
    • +
    +

    结论

    +
      +
    • 游戏在某种程度上要求甚至依赖玩家能动性的积极发挥。对玩家能动性的关注将引导我们重新评估对以下问题的常规臆断:游戏是什么?谁在生产它?怎样生产?
    • +
    +

    电影、改编与电脑游戏

    +

    恐怖类型——从电影到游戏

    +
      +
    • 恐怖类型携带者“所有口头叙事的印记:主题和母题的自由转换、原型人物和背景设置,以及不断累积的续集、翻拍和模仿。某种意义上,在这个领域当中,没有原创,没有真实的或正确的文本,而只有变体。”
    • +
    +

    改编与《突变怪物》

    +
      +
    • 不可预知的威胁乃至有时无法辨识的敌人及其带来的焦虑,正是为玩家提供的挑战,玩过经过训练即可将之战胜。在从电影到游戏的移植中,其功能和意义均发生了改变。
    • +
    • 游戏设计中最为重要的,是对空间和空间当中物品的设置与安排。
    • +
    • 为已经开发出来的游戏增加一个额外关卡是一回事,而尝试去想象一个尚不存在的游戏的某个关卡是另一回事,后者要困难得多。
    • +
    +

    人物塑造与过场动画

    +
      +
    • 一款好的游戏需要为玩家提供有意义的选择,以及相应可觉察的成果。《突变怪物》的叙事性元素给游戏的目标、任务和障碍提供了背景。
    • +
    • 恐怖游戏的操作应当足够简单,因为这会在相当程度上将玩家限制在其所处的环境中,增加游戏的悬念。
    • +
    • 过场动画分为两种:预制的和实时的。必须对带有预制过场动画的游戏进行周密计划,以避免其对游戏世界的真实感造成破坏。
    • +
    +

    改编——从游戏到游戏

    +
      +
    • 目标是开发一个具有足够普适性的游戏引擎。
    • +
    +

    游戏与性别

    +

    作为再现的游戏

    +
      +
    • 有研究者认为,女性对电脑游戏的疏离主要是因为其再现性因素——游戏中女性化身的外形。
    • +
    • 在很多游戏中,被特别标志的或被边缘化以至于缺席的性别,都是女性。
    • +
    • 如果将电脑游戏视为一种文本,那么将玩家定义为一种性别主体并不可行。
    • +
    +

    娱乐性与再现性

    +
      +
    • 一些游戏强调视觉呈现和故事讲述方式——它们在引人入胜的细节中呈现游戏的场景和角色,我们将之称为游戏的再现层面。
    • +
    +

    性别、游戏与语境

    +
      +
    • 游戏开发商或其发行商如果要争取更多的女性玩家,就要面临失去现有男性玩家的危险。
    • +
    • 那种认为女性对电脑游戏不感兴趣的想法和剥夺女性玩游戏权利的做法已经过时了,尽管某些社会因素和文化因素还将因用户的性别而持续对其产生影响。
    • +
    +

    游戏分析的实践

    +

    定义游戏

    +
      +
    • 目标、障碍、资源、奖赏和惩罚等是电脑游戏的一些重要元素,同时还有一些可获得的不同种类的信息——不管是对于所有玩家,还是对于个别玩家,抑或对于游戏本身——以及这些信息逐渐显露的诸多方式。
    • +
    • 我们也关注到其他有助于探索游戏要素多样性的理论研究方法, +
        +
      • 例如,可以探究游戏当中的奖赏与惩罚(游戏的“经济体系”)或游戏当中玩家可控因素与不可控因素之间的制衡关系。
      • +
      • 同样,也可以探索游戏中不同类型的障碍物
      • +
      • 还可以分析不同类型的游戏规则
      • +
      • 它们为我们发现一款游戏的与众不同之处,并促使我们继续思考这种差异是有意义的创新还是对规范的偏离。
      • +
    • +
    • 所有的定义都聚焦于被我们所称的游戏的娱乐层面或“游戏系统”,是它们决定一款游戏是不是可玩,以及游戏活动所遵循的限制。【游戏系统决定了游戏的定义。】
    • +
    • 对电脑游戏的分析既要关注“娱乐性”,也要关注“再现性”,以及二者之间的关系。
    • +
    +

    游戏类型

    +
      +
    • 在一个典型的动作冒险游戏中,我们通过扮演游戏内设定的化身来进行游戏,该化身通常会依循特定的游戏顺序克服已被设定的的重重困难。游戏目标通常相当明确,其经济体系一般与可量化的特征相关,比如生命值和弹药。游戏玩法中最核心的是速度和精确性。玩家会在游戏过程中学习一些技巧,但化身却不会成长。【游戏路线是既定的,游戏目标是明确的,游戏玩法是速度和精确度。】
    • +
    • 与之相反,RPG游戏的主角可能是玩家通过一系列选择构建出来的,而且围绕该角色通常会有一个具有特定技能的辅助性团队。玩家可以自由地在游戏中探险,展开各种探索,并且可以不用遵照预设地步骤战胜游戏中的各种障碍。虽然游戏最终目标可能非常明确,但通常会有一些与最终目标未必相关的支线目标。以经验值和物品清单呈现出来的经济体系十分重要,但它更倾向于随玩家的表现而浮动。玩游戏的关键在于策略而非速度,而游戏体验往往具有更多反思性。【游戏路线是未定的,游戏目标是多样的,游戏玩法核心是策略。】
    • +
    • 这两者之间的关键性差异与游戏系统有关,而非在再现层面。
    -

    角色控制与角色移动之间的关系被称为控制参考帧(control -reference frame)

    -

    Chapter 2: Camera -Fundamentals

    -

    Real-World Cameras

    -

    真实世界的相机大概包含以下关键要素:

    +

    叙事与游戏

      -
    • Lens type
    • -
    • Capture method
    • -
    • Film stock
    • -
    • Capture rate
    • -
    • Exposure of film
    • -
    • Projection method
    • -
    • Aspect ratio
    • +
    • 沉浸分为感知上的沉浸与心理上的沉浸,卷入则是玩家被迫在游戏中采用一种更为审慎和具有反思性的态度。
    • +
    • 沉浸和卷入并不互相排斥,二者间的摆动在游戏“心流”体验的产生中处于核心地位。所谓心流体验,就是玩家感到自己此刻“处在巅峰状态”,这种及其满足和欲罢不能的感觉部分由玩家在沉浸和卷入之间的转换所触发。
    -

    真实世界相机的一些特点:

    +

    穿越游戏空间

      -
    • 缺乏交互性
    • -
    • 预先决定的机位
    • -
    • 相机镜头
    • -
    • 场景切换
    • -
    • 胶片曝光
    • -
    • 后处理特效
    • +
    • 等距视角的RPG游戏更多依赖心理上的沉浸,而《寂静岭》中的移动视角和3D效果唤起玩家感知沉浸。
    -

    游戏相机的一些特点:

    +

    游戏动力学

      -
    • 模拟真实世界相机的功能
    • -
    • 不需要相机实体
    • -
    • 动态变化
    • -
    • 允许游戏世界的不同投影
    • -
    • 可变的光照与渲染
    • -
    • 更多样的转场
    • -
    • 允许特殊效果
    • +
    • 为什么游戏攻略大多采用第二人称祈使语气?因为游戏攻略大都紧密围绕游戏系统撰写,几乎不会注意再现层面,攻略是冷静的和技术性的。粉丝撰写的小说和诗歌都着墨于游戏的再现层面,倾向于借鉴游戏的过场动画,而非游戏本身的互动性元素。
    -

    一些专有名词:

    +

    走向线上

      -
    • Game system: -管理相机、应用相机、控制相机行为、生成用于渲染的信息、控制参考帧
    • -
    • Game camera: 抽象的游戏实体
    • -
    • Presentation style: 指将游戏世界投影到屏幕空间的方法
    • -
    • Camera behavior: 一人称/三人称相机,影院式/交互式相机
    • -
    • Look-at position: 相机朝向的位置
    • -
    • Desired position: 相机移动的位置
    • -
    • Orientation: 相机朝向
    • -
    • Desired orientation: 相机的目标朝向
    • -
    • Rotation: 朝向的变化量
    • -
    • View frustum: 游戏世界的可视空间
    • -
    • Viewport: 呈现在显示设备上的一种数据结构
    • -
    • Field of view: View frustum的上下/左右边界的夹角
    • -
    • Aspect ratio: 长宽比,对于standard definition television -(SDTV)来说,是4:3,对high-definition television -(HDTV)是16:9。对电影投影中使用最多的是1.85 (CinemaScope)和2.35 -(anamorphic)
    • -
    • Refresh or frame rate: 刷新率
    • -
    • Refresh and motion blur: -刷新模糊指显示设备刷新率不能与计算机的输出同步,运动模糊是因为物体运动快于曝光时间
    • -
    • Display tearing: 显示撕裂
    • -
    • Player character: 玩家在游戏中控制的物体
    • -
    • Target object: 被看向的物体
    • -
    • Interpolation: 插值
    • -
    • Projection: 投影
    • -
    • Parallax: 视差
    • -
    • Transitions: -转场,包括过程化转换、插值、瞬时切换、清除、过渡等等
    • -
    • Camera constraints: 相机约束
    • -
    • Motion constraints: -运动约束,如距离、相对位置、路径、速度、加速度等等
    • -
    • Orientation constraints: 通常用于第一人称相机
    • +
    • 玩家玩在线游戏的动机与游戏的娱乐层面和再现层面都有关系。 +
        +
      • 再现层面的动机包括操纵视觉形象、创建角色和生成叙事。玩家构建化身的方式将折射出其现实生活的某些方面,或其内在愿望和幻想,而参与游戏的模式反映出不同的社会动机。
      • +
      • 娱乐层面的动机则更多和游戏活动本身直接相关,涉及竞争、规则和游戏目标。
      • +
      • 此外还有公共动机,即玩家之间的互动,这是一种多模态呈现的现象。
      • +
    -

    Camera Presentation Styles

    -

    Presentation style -通常被划分为正交(2D)或者透视(3D)渲染,有时也会使用2.5D

    -

    Camera Behaviors

    -

    主要有三种相机行为:

    +

    游戏中的社会生活

      -
    • Cinematic cameras: -非交互的、玩家不可控的相机,包括cutscene和real-time cinematic -sequences
    • -
    • First person cameras: -视野比TP游戏小,使玩家难以全面捕捉空间与环境信息,跳跃也更加困难。在实现FP相机时,建议把相机与任务的头部分开,同时使相机的朝向独立于玩家的朝向
    • -
    • Third person cameras: -允许更大的视野,适用于环境导向型游戏,但存在world navigation与collision -avoidance两个问题
    • +
    • 在团队协作中,游戏的娱乐层面是主要的。
    • +
    • 游戏具有三个能动性:个人能动性、代理能动性、集体能动性。
    -

    此外,相机也可以分为predictive和reactive两类: - Reactive: -根据游戏对象的变化而变化 - Predictive: -根据当前游戏物体预测最佳的相机位置和朝向

    -

    Path finding solutions: - -全局路径搜索:搜索环境中的一条路径,能够保持美学质量 - -局部路径搜索:以某种具体的物体类型为核心进行搜索 - -基于目标的搜索:在各种约束下搜索路径

    -

    View Generation

    -

    视图生成步骤:

    +

    生产游戏、生产意义

      -
    • 游戏中每个活跃的物体更新内部状态
    • -
    • 相机更新位置、朝向等信息
    • -
    • 对游戏中待生成的每个view: +
    • 游戏设计者最为关心的问题,正是游戏的娱乐性和再现性之间的相关性和互联性。比如,角色具有再现性和娱乐性,过场动画也有娱乐性从(提供了战略上必不可少的信息)。
    • +
    +]]>
    + + 游戏 - 游戏理论 + + + 随笔 + 游戏 + +
    + + ComponentCameraSystem - A Simplified Camera System for You to Create Plentiful Gameplay Camera Movements and Effects + /2023/02/09/23/21/ + ComponentCameraSystem is a simplified, extensible +and designer-friendly camera system for Unreal Engine. It enhances the +built-in spring arm and camera components in native Unreal editor across +a wide variety of common gameplay camera behaviours such as keeping a +target at a fixed screen position, moving on rail, and resolving +occulusion in complex occasions, enabling you to easily create plentiful +smooth camera movements and effects within only few minutes. Go to the +Documentation +for more details.

    +

    Currently ComponentCameraSystem supports Unreal +Engine versions >= 5.0. So before using this plugin, please upgrade +your project to Unreal Engine 5.0 at its minimum version +requirement.

    +

    You can buy this plugin at Unreal Marketplace. +Persistent upgrade will be made to make it more stable and support more +features.

    +]]>
    + + 游戏 - 相机 + + + 计算机 + UE + 相机 + 游戏 + 插件 + +
    + + 无穷连根式求极限的充要条件 + /2020/09/09/18/52/ + 在刷习题集或者考试的时候我们经常会遇到诸如或者的极限求解或极限存在性证明。解决此类问题的方法有很多,但都可以归结为一点:缩放。要么是两端缩放然后夹逼定理,要么是证明有界然后两边取极限。本文记录此类问题极限存在的一个充要条件,以供参阅。

    + +

    Ramanujan's Problem

    +

    这类问题最著名的是拉马努金(Ramanujan)所提出的恒等式:

    +
    +

    证明:

    +
    +

    这个等式的证明是简单而有趣的:

    +

    +

    同时,Ramanujan还断言下面的结论:

    +

    +

    这个的证明也是容易的。首先把上式和(1)式比较,就发现上式以3为上界,并且由单调性可知,其极限是存在的。为了证明的极限就是3,我们证明:对任意的,都存在,使得所有的都有

    +

    现在任取,令,故,故有:

    +

    +

    乘进去,就有:

    +

    +

    由于,且存在,当时,有

    +

    +

    把这些全部带入(2)式,就可以证明:

    +

    +

    从而完成证明。

    +

    Polya's Criterion(Polya准则)

    +

    在考虑普遍情况下首先来观察一些特例,一个典型的特例就是形如的无穷根式,每个根号的次数都是,或者幂次都是。对于来看,控制它的幂次是,对于来说,控制它的幂次是,对于来说,控制它的幂次是。假设这个无穷根式极限存在,那么我们关心的肯定是足够大时,它被什么控制,显然是及控制它的的幂次。所以,一个合理的猜测是,如果极限存在,那么该根式就收敛。下面我们将看到,这个猜测已经非常接近“真相”,甚至是真相的一部分。

    +

    Description

    +

    设序列,则可以用下述条件判定:

    +

    +

    上述准则还可以进一步推广为:

    +
    +

    序列收敛的充要条件是: +

    +
    +

    注意到,这个极限可以取有限数或者负无穷。我们将在陈述下面的定理一之后进行证明。

    +
    +

    (定理一) 序列收敛当且仅当存在有限上极限 +

    +
    +

    首先证明必要性,即假定收敛。因为,故一定是有限的,得证。

    +

    再来证明充分性。假定,则存在使得对所有,有,因此。从而有:

    +

    +

    同时又因为

    +

    +

    从而有,又由的单调性知收敛。

    +

    到此为止,我们发现定理一和开始我们的猜测是非常相似的,只是定理一只需要上极限,这比我们的猜测更加宽松。下面我们利用此定理证明Polya's +Criterion.

    +

    Proof of Polya's Criterion

    +

    时,存在,当,也即,由定理一收敛。

    +

    时,存在某个,对某些无限的,使得,也即。因此,对这些而言:

    +

    +

    因此.

    +

    最后考虑

    +

    收敛,则有限,即存在对所有成立,因而。此时若,则

    +

    +

    而当时,按照约定有,则综上公式(3)的必要性得证。

    +

    同时公式(3)也是充分的。假定条件成立但不收敛,则由定理一,则对任意的,有充分大的使得,从而,于是有

    +

    +

    这说明上极限是无穷大,与假设矛盾。充分性得证。

    +

    Examples

    +

    例一

    +

    现在我们考虑一个序列,当的时候,它的上界是,而后者我们上面已经证明了它的上界是2。现在我们考虑的情景。此时有:

    +

    +

    所以我们证明了,对,序列都是收敛的,并且没有使用定理一

    +

    例二

    +

    现在考虑下述恒等式:

    +

    +

    于是可以立即得到:

    +

    +

    现在令,就有:

    +

    +

    替换,有:

    +

    +

    两边约去2,就有:

    +

    +

    例三

    +

    由余弦二倍角公式可知:

    +

    +

    此时令,就有:

    +

    +

    所以可以立刻得到下式的极限:

    +

    +

    Herschfeld’s +Convergence Theorem (Herschfeld收敛定理)

    +

    Polya's Criterion只考虑了指数为的情况,对于更加普遍的情况,即形如的序列,Herschfeld’s Convergence +Theorem给出了一个其收敛的充要条件。

    +

    Herschfeld’s +Convergence Theorem 告诉我们了一个无穷根式收敛的充要条件:

    +
    +

    设序列且级数收敛,则序列收敛的充分必要条件是:

    +
    +

    这个定理的证明之后有空了补充。

    +

    后记

    +

    实际上无穷根式分为右无穷根式(Right Infinite +Radicals)和左无穷根式(Left Infinite +Radicals)两种,这里重点讨论的是右无穷根式的情况,左无穷根式可以仿照进行推导。

    +]]>
    + + 数学 - 数学分析 + + + 数学 + 随笔 + +
    + + 巫师三:一个告诉你原来游戏可以是艺术品的神作 + /2020/10/09/19/34/ + 巫师三是一款已经被时间证明的,当之无愧的神作。巫师三没有波澜壮阔气势浩天的景致,却有风格迥异人文色彩浓厚的地区特色。威伦的破败荒凉贫穷凄苦,为了一双鞋可以连父母都能出卖;诺威格瑞的大城风范,有日进斗金的土豪乡绅,也有困守城中的穷困百姓,女巫猎人穿梭其中,种族矛盾暗流涌动;史凯利杰群岛的剽悍民风,对外来者的敌视,对入侵者的抵御,捍卫土地的决心让诸多岛屿团结在一起;凯尔莫罕的静谧安详,森林环抱狼堡,猎魔人镇守故乡,仿佛出世的仙境,危机四伏但又简单质朴;陶森特的梦幻美丽,人们自得其乐,生活在这宛若仙境的城都中。不仅仅是景色,巫师三更是以它的剧情和对人物的刻画出名,抛开难以入流的战斗系统,单凭巫师三对剧情的把控、对人物的描写和对画面的雕琢,就足以令这部游戏称为艺术品。在经历了完整的主线和DLC流程后,几乎没有玩家会给巫师三一个低分,尽管它有瑕疵,但是它在剧情、人物和画面上已经到达巅峰造极的水平了,更难能可贵的是,这三者完美融合,在体验游戏的同时,给予玩家美的享受、身心的震撼。这是奥德赛无法比拟的,也是大多数游戏难以企及的高度。

    + +

    前言

    +

    由于巫师三的世界观过于宏大,一篇未经雕琢的读后感不能道尽巫师三的所有优点,故本文旨在从宏观上呈现巫师三是如何完美统一任务、节奏与剧情这三者的。至于其他的,我们有机会留在以后再说。我相信,通关巫师三的玩家,一定会承认巫师三在任务的组织和剧情的展开上非常连贯丝滑,而且剧情上的节奏递进、叙述上的跌宕起伏都让人回味无穷。游戏本质是在讲故事,但是巫师三成功地给予了玩家沉浸式的体验,也就是,我们从观众的角色深入到了主角的角色,从游戏外的世界走进了游戏内的世界,仿佛每个人都是真实可及的,都是有血有肉的,都是有情有义的。那么,如何讲好这个故事,把观众代入到游戏中,就是巫师三不同于其他游戏的关键所在,这也是这篇玩后感的主要内容。

    +
    +被附身的Geralt帅中泄露出一丝痞气 + +
    +

    主线为林,支线为溪

    +

    巫师三的一大特色就是它的任务系统非常丰富,从表层看,巫师三的主线只有一个——寻找女儿,但是,在每个地区,都会有非常多的支线为主线提供背景。就好比一个建筑,主线是骨架,支撑起整个游戏的大致轮廓,而支线是钢筋水泥,垒起了整个建筑的最终格局。当然玩家可以只做主线不做支线,但我相信如果不做支线,这个游戏的乐趣就会少一半。

    +
    +父女之间的亲昵 + +
    +

    说到支线繁多,一个典型的对比就是刺客信条奥德赛。同样有非常多的支线,但是从我个人的游玩体验来说,我宁愿无视奥德赛的90%的支线,直接一条路走完主线,但是巫师三的支线不然,玩家会有强烈的意愿去做大部分支线,在做支线的过程中解锁问号,发掘游戏中隐藏的剧情,是巫师三最为亮眼的一大特色。如果要对比巫师三和奥德赛的任务系统,可以从下面几个方面评价:

      -
    • 决定相机的位置和朝向
    • -
    • 决定显示设备上的viewport
    • -
    • 决定游戏世界中的frustum
    • -
    • 给定frustum,决定哪些物体被剔除
    • -
    • 对每个待渲染的物体: +
    • 支线多样化:巫师三的支线是多样的,支线之间没有重复,猎魔人委托可能会更无聊一点,但生意人的事情嘛,不寒碜。除了猎魔人委托之外,寻宝任务有解密探险要素,区域日常支线有搞笑奇葩要素,重要支线甚至会改变整个巫师三世界格局。然而,奥德赛的支线是单调的,支线的选择不会影响主线的进程,支线品类非常单一,并且缺乏逻辑。巫师三支线给玩家的动力是充分的:委托赚钱,寻宝得装备,日常支线放松一下,重要支线扣人心弦;而奥德赛不是的,除了几个从头到尾都有的支线之外(类似于巫师三的寻宝任务),其他的支线毫无意义,既对主线没有补益,而且也缺乏特色。
    • +
    • 支线重要化:当然,支线多样丰富是一方面,更重要的是,支线是需要起到作用的,有时候甚至是决定性的作用。巫师三的主线和支线的分工相当明确:主线用来推进整个游戏进程,而支线则负责主线缺失的娱乐性与严肃性。这大大补充了单主线的贫乏,让玩家在游戏的时候不会觉得推进主线是无聊的,因为在同时还有大量丰富有趣的支线在支撑游戏的饱满度。而且,巫师三的支线并不是其他游戏中如过往云烟般的支线,而是不同的支线会在主线的不同时间点产生决定性的影响。比如,迪胖的支线会决定整个游戏最终的政治格局,这是从大了讲。从小了讲,如果细心留意,就会发现很多很多支线都涉及到游戏中不同人物的命运,即使是路人,我们也能够感到他们是真实的,而不仅仅是一堆数据。这些都是支线带来的重要积极影响。巫师三赋予了支线游戏的精神内核,并令其成为承载游戏厚度的“实力担当”。
    • +
    +
    +这样有趣的小支线在巫师三里不胜枚举 + +
      -
    • 变换物体
    • -
    • 应用光照着色
    • -
    • 2D投影
    • -
    • 光栅化
    • -
    - +
  • 支线惊奇化。巫师三的支线时常给人一种惊奇的感觉,无论是之前无意之中做的支线会在后来的某一天发现原来会产生这样的影响,还是后来做到的支线会发现是之前剧情的关键结果,这种突如其来的“因果”呈现都带给玩家游戏体验上的冲击。重点是,正所谓大音希声,这样的惊奇化支线完全不需要漫长的铺垫,而只需要一个小小的“接口”,把因果串联起来就可以达到这样震撼的效果。一个典型的例子就是在DLC石之心里,拍卖行里的一个小支线展现了维瑟米尔的不为人知的一面,不但极大丰富了人物形象,而且还带给玩家无尽的唏嘘。此类四两拨千斤的支线在巫师三里比比皆是,
  • -简化的渲染流程。 - +一个非常小的细节,但是却折射出了两个饱满的人物形象 + +
    +
    +一个非常小的细节,但是却折射出了两个饱满的人物形象 + +
    +

    所以,巫师三成功的一大关键因素就在于他巧思的任务系统,尤以支线为代表,主线与支线的交织,主线推动整个剧情的发展。支线却承载了多样化、重要化和惊奇化的具体内容,让人家流连忘返,深陷其中。

    +

    选择困境,人生模拟

    +

    当然,除了巫师三之外,还有很多游戏的支线也很丰富,主线支线的组织也很巧妙,那凭什么巫师三这么牛逼呢?这就是巫师三的第二个牛逼之处:通过合逻辑的选择带给你极尽真实的人生体验。

    +

    巫师三的任务绝大多数都是需要玩家做出选择的,无论主线还是支线,这在从威伦到诺威格瑞之后尤为突出。不同于很多游戏可有可无的“可选项”,巫师三的每一个选项都是经过反复斟酌的,不存在无意义的选项。威伦雷索的选择直接决定了他之后会不会在凯尔莫罕的时候来帮你;凯拉的连续选项直接决定了她的命运,也决定了兰伯特的命运(这个有些小伙伴可能还不知道);威伦有个狼人的支线直接决定了这个悲惨爱情故事的结局;诺威格瑞迪胖的支线决定了游戏最终的政治格局。对主线来说,这样的决定性选项无处不在:血腥男爵的选项,特莉丝和椰奶的选项,Ciri的一系列选项等等。在巫师三中,选项的重要性直接以前后的因果关系体现出来,也许你的不经意的一个选择,就很有可能在未来决定了不同的世界线,其中的很多选项,你甚至都意识不到是由自己的决定造成的,这是不是有《命运石之门》的感觉了。

    +
    +Ciri猎魔人结局片段 + +
    +
    +Ciri猎魔人结局 + +
    +

    我们之前说了,巫师三带给玩家的是沉浸式的体验,那实现这一点的关键就在“真实”。这里的真实,大部分是成年人的部分:我们会遇到各种各样的决策,尽管有句话是说“小孩子才做选择,成年人全都要”,但实际上,只有小孩子才能全都要,成年人的世界不能兼得鱼和熊掌。有些选择比较简单,但也有很多选择,可以说是直接决定了往后人生的整个发展方向。世界上没有后悔药,所以我们常常看到成年人的无奈与悔恨。但是游戏不一样,我们可以回档,可以重来,可以把现实中无法完成的夙愿转移到游戏中,体味人生抉择的无奈,尝尽人生起伏的精彩,但最爽的,还是能够重来,再走一遭。

    +

    巫师三是在真正意义上模拟了人生: - +首先,它的选项逻辑是合理的。很多游戏,包括奥德赛在内,它们所谓给出的选项很多都是不合理的。我为什么要这么做?为什么只有这几个选择?策划是不是脑子有坑?正常人会这么做?在选择的时候,不合理、不充分的选项不但不能产生真实的沉浸感,而且还会极大破坏游戏剧情推进的合理性。巫师三不然,它给出的每一个选项都是合理的,都是玩家真的可能在现实生活中面对的两难困境,从而不会让玩家产生“这个选项好弱智”的疑惑。真实的选项才能获得真实的体验,为选择而选择的设置只能适得其反。 +- +其次,它的选项是有反馈的。所谓反馈,就是无论你选了什么选项,都能在游戏的过程中获得该选择的结果,而且在看到这个结果的时候,玩家是可以或多或少发现它的起源就是那个选项。巫师三的选项就是有始有终的,它可能在有意无意间唤起你的决定,或最近,或久远,那个时候,玩家就会惊叹:原来如此!这是很重要的,因为在现实中,人们会感到“后悔”或者“幸好”,就是这样的“记忆唤醒”在作祟。人是健忘的,但这不是说记忆会消失,而是会被暂时隐藏,所以必须要有一个触发点(Trigger)才能重新唤醒这段记忆,而记忆一旦唤醒,就会有首尾呼应的触动,进而给玩家带来震撼感。这也是巫师三能带来真实体验的一个重要因素。

    +
    +Geralt和夏妮 +
    -

    在上述步骤中,与相机系统直接相关的有:view frustum, view -transform和projection transform

    -

    Player Controls

    -

    A control reference frame refers to a relationship between changes to -the controller input and how it affects movement or other aspects of -player control.

    -

    在FP游戏中,control reference -frame直接对应了玩家角色面朝的方向,且通常只能绕up -axis旋转。在TP游戏中,control reference -frame基于相机与玩家的相对位置。

    -

    Control reference frame需要始终反应玩家的意图

    -

    Chapter 3: Cinematography

    -

    Nomenclature

    -

    电影学中的一些专有名词:

      -
    • Dolly: 指平行于地面的移动,dollying in是向前推进,dollying -out是向后推进,crab left是向左移动,crab right是向右移动
    • -
    • Pan/Yaw: 绕着camera up轴的旋转
    • -
    • Tilt/Pitch: 绕着camera right轴的旋转
    • -
    • Tracking: 跟着物体移动
    • -
    • Depth of field: 景深
    • -
    • Color fade: 颜色渐变
    • -
    • Lens flare: 镜头眩光
    • -
    • Cut-away shot: 切换到另一个镜头用于强调原场景中的某个元素
    • -
    • Insert shot: 切换到场景中的细节部分,比如人物特写
    • -
    • Jump cut: 瞬间切换
    • -
    • 180 degree rule/line of action: 二人镜头应该让相机保持在同一侧
    • -
    • Point of view shot: 第一视角镜头
    • -
    • Crane shot: 第三人称视角
    • -
    • Reaction shot: 拍摄人物反应的镜头
    • -
    • Reverse shot: 与前一个镜头角度相反,但仍遵循180度法则
    • -
    • 30 degree rule: 当使用jump -cut时,新镜头与上个镜头的角度差应该大于30度,否则会让观众认为是场景的物体发生了移动而非相机
    • -
    • Forced perspective: -强制透视指一系列技术让观众产生错误的视觉效果,如大小、位置关系
    • +
    • 最后,它的选项是关键的。所谓关键,就是一些选项会产生极其重大的影响,此类选项在主线中最多,但也不乏一些支线。为什么说一些选项需要具备关键性?其实这就像欣赏任何一个文学作品一样,无论是小说,还是电影,剧情总是需要跌宕起伏。一平到底,或者过于波折,都会给观众造成审美疲劳。这在游戏里也是一样的。如果所有的支线都是做了就做了的那种,那么就不会给人留下深刻印象,反之,只有平淡与冲突交错的支线才会充分勾起玩家的兴趣。上面我们说了巫师三的支线都是有反馈,那么反馈对后续游戏造成的影响就可以定义为该支线的“关键度”。一个比较合理的关键度分布,应该需要满足5:3:2或者6:3:1的比例的,也就是大部分任务都是比较平淡的,立马能得到反馈的,而且对主要剧情没有影响的;少部分任务会在游戏后续呈现出比较重要的影响;而极少的任务会产生决定性的影响。举例来说,委托任务,城中任务,基本上都可以归为第一类,像凯拉、雷索这样的任务可以归为第二类,最后像迪胖、希里这样的就可以归为第三类。可以看到,巫师三的选项是非常有层次的,这样的层次感不会让玩家感到枯燥疲倦,从而更好地代入到游戏中。这也是巫师三让人停不下来的一个原因之一。
    -

    Dynamically -Generated Movies/Replay Cameras

    -

    为了实现回放,可以记录初始游戏状态和玩家操作,然后模拟行为。一些回放系统还支持观测者视角。

    -

    有三种形式的回放相机:reproduction, scripted, dynamically -generated:

    +
    +Geralt的回忆 + +
    +

    有条不紊,循序渐进

    +

    在前面的两节中,我们说了很多支线的内容,包括支线的特性、选项的特性,但是在游戏里,决定玩家游戏体验是否“丝滑”的决定因素是主线的推进节奏。巫师三的主线的推进非常顺滑,玩家不会感到节奏上的拖沓或者赶工,从百草园,到威伦,到诺威格瑞,再到Skellig、凯尔莫罕,每个地方的风土人情都在剧情中得以充分展开,每个地方,从开始到结尾,都有明显的渐变,不会造成突兀感。这就是主线承接上的无缝性。

    +

    从整体上来看,巫师三的剧情推进呈现出前期平缓,后期陡峭上升,在最后达到高潮之后迅速下降收尾的趋势,尤其是在最后经过高潮后的突然结束,让玩家回味无穷。从百草园到是Skellig是整个游戏的平缓期,在这个时期,玩家更多地是在品味一个新地点的独有风格,所以游戏没有安排太波折的剧情,只是一点一点让玩家代入到整个游戏的剧情中。之后从凯尔莫罕开始,剧情开始迅速推进,寻找Ciri——>找到Ciri——>凯尔莫罕保卫战——>决战狂猎——>真相浮出——>Ciri献身,这一系列重要剧情节点被安排地非常紧凑,这是因为经过前中期长时间的铺垫,玩家已经全然进入到角色,当前最期待的是一波快节奏的剧情轰炸。笔者在出Skellig之后,就有预感剧情一定会出现巨大转折,从乌马开始,到狂猎结束,整个线非常完整,发展也非常迅速。但在这样的快节奏下,游戏不是混乱的,而是始终围绕着一个线索在有序地推进(找Ciri——>干狂猎),这样一来,即使游戏的节奏突然加快,玩家也没有不适感。

    +
    +老爷子战逝 + +
    +
    +老爷子死去Ciri流泪 + +
    +

    巫师三的三个时期“平缓——上升——下降”是处理得很好的。平缓期大概占了整个游戏进度的50%左右,在这个期间,游戏的主要内容缓慢推进。注意,不是没有推进,而是以叙述的方式娓娓道来,逐渐营造引人入胜的氛围。上升期大概占了45%的内容,在这个阶段内,游戏的剧情迅速发展,剧情节点迅速变化,游戏状态迅速切换,然而玩家没有任何的不适应,反而更加乐在其中,停不下来。这是因为前50%的铺垫已经将玩家去完全代入到游戏中,仿佛你就是Geralt,你的使命就在眼前。在上升期的末尾,游戏戛然而止,来到下降期。巫师三的下降期非常短暂,我认为大致可以从Ciri献身开始算做下降期,在这个节点,玩家之前所有的选择都会体现在不同的结局上,突然结尾更加凸显了玩家之前决策带来的种种后果。显然,CDPR没有采取让游戏慢慢结束,而且一下子就关闭了游戏主线剧情,带来了一种“惊愕感”:“这游戏结束了!?”这样的设计是很巧妙的,但也是建立在之前的平缓期与上升期的铺垫之上的。可以说,巫师三这种有条不紊、循序渐进的三阶段推进方式,是它能够取得沉浸式体验的另一大重要原因。

    +
    +通关主线,从空荡的凯尔莫罕重新出发,寂寥涌上心头 + +
    +

    写在最后

    +

    尽管本文的目的在于从任务系统上简要感受一下巫师三为什么能带给人沉浸式的体验,但是对于巫师三来说,画面、人物、音乐等等其他方面都是促成巫师三能成为神作的重要因素,尤其是巫师三的音乐,直击人心,给人带来的沉浸感不亚于剧情,在此强烈推荐。

    +
    +Geralt太可爱了吧 + +
    +

    之后我会介绍一下英灵殿和12月份的赛博朋克2077,到时候再来看看,巫师三这款5年前的游戏是否仍有一战之力。

    +
    +陶森特美丽的风光 + +
    +]]>
    + + 游戏 - 玩后感 + + + 随笔 + 游戏 + 生活 + 巫师三 + +
    + + 《游戏设计的236个技巧》笔记 + /2021/08/17/01/07/ + 本文是《游戏设计的236个技巧:游戏机制、关卡设计和镜头窍门》的笔记。

    + +

    前言

    +

    一款优秀的游戏是如何让玩家在某个瞬间感到无比有趣、极度畅快的呢?本书旨在引领读者发现“让游戏更有趣的设计技巧”。 +本书将内容分为“玩家角色”“敌人角色”“关卡设计”“碰撞检测”“镜头”五个部分。

    +

    让3D游戏更有趣的玩家角色技术

    +

    能够吸引2D游戏玩家的3D游戏设计技巧(《超级马里奥兄弟》《超级马里奥3D大陆》)

      -
    • Reproduction cameras/keyframed cameras: -在固定的时间间隔记录位置和朝向信息,实现简单但可能依赖于其他非keyframed游戏事件
    • -
    • Scripted cameras: -是可交互与预定义的结合。Replay相机的位置可以采用下述的形式: +
    • 马里奥飞奔的感觉让人很舒服。
        -
      • Discrete camera behaviors: 此类相机perform jump -cuts或者预定义相机位置之间的插值
      • -
      • Path-based motion: 使用设计师预定义的相机路径
      • -
      • Slaved motion: 根据特定物体设置相机位置 -类似地,相机的朝向(甚至FOV)可以采用如下的形式:
      • -
      • Fixed orientations: 固定朝向
      • -
      • Pre-defined orientations: 预定义朝向
      • -
      • Object tracking: 跟踪游戏物体调整朝向
      • -
      • Object framing: -保证游戏物体保持与显示设备一致的相对距离,此时物体的显示大小可以通过改变FOV调整
      • +
      • 按住十字键开始加速,即使放开十字键也不会马上停住,而是有惯性。
      • +
      • 按B键冲刺的过程中按反方向键,就会出现一个急刹车的动作。
    • -
    • Dynamically generated cameras: -在回放的时候动态生成相机,如死亡视角、高亮操作等等 +
    • 但是塞尔达中林克就没有惯性,让玩家精准停在想停的位置。
    • +
    • 这是因为游戏想带给玩家的游戏体验不同。
        -
      • 死亡视角相机:可以通过一个围绕角色的stationary相机实现,或者使用一些预定义的相机位置/朝向
      • -
      • 重生相机:当玩家进入/重进游戏时播放的相机
      • +
      • 塞尔达的游戏体验是探索与战斗:不需要过多关注奔跑的细节动作。
      • +
      • 超级马里奥的游戏体验是单人挑战障碍赛跑:关注如何更快奔跑、转弯、准确停止并穿越障碍物。将“无法简单停止”作为一种操纵上的风险加入游戏,除了能给游戏带来紧张感之外,还能让玩家在熟悉操作之后获得成就感。
    -

    一些常用的相机特效:

    +

    B键冲刺带来的感官刺激以及风险与回报的趣味性

      -
    • Reorientation changes: -引入朝向误差,模拟人类持相机的行为,但要注意摇晃的幅度
    • -
    • Camera shake: -相机抖动通常在渲染过程中实现而非游戏世界中,因为这不会改变相机的实际位置,注意在第三人称游戏中应当控制相机抖动的幅度和频率
    • -
    • Roll: 用于营造失控的感觉,比如用在赛车游戏中
    • -
    • FOV/DOF: 注意DOF不要频繁使用
    • -
    -

    Scripting

    -

    Scripting通常指设计师手动指定相机的位置和朝向,要么在编辑器中进行编辑,要么使用Maya等工具编辑后直接导入

    -

    一个典型的scripting system应当包括:

    +
  • 操纵冲刺中的马里奥会给玩家带来畅快感。
      -
    • 调整相机位置和朝向
    • -
    • 转场
    • -
    • 相机插值
    • -
    • FOV变化
    • +
    • 通过运动或动作获得感官上的舒畅体验的过程称为感官体验
    • +
  • +
  • 有趣秘密还在挑战中蕴含的风险与回报。 +
      +
    • 马里奥通过加速的移动实现了风险与回报,玩家按住B是一种主动提高风险与回报的行为。
      +
    • +
  • +
  • 在超级马里奥兄弟之前,动作类游戏的跳跃一直都是跳跃到固定高度,但是超马采用了按键时长与跳跃高度相关的机制。
  • +
  • 在跳跃时按方向键可以控制马里奥在空间左右移动。
  • +
  • 在跳跃过程中碰到墙壁等障碍物不会下落,这虽然有悖物理,但是给了玩家更舒适的游戏体验。
  • -

    Scripting system最好还支持编辑时预览,尽管非常困难

    -

    Editing

    -

    编辑就是把所有的shot重新组织、拼接的过程,主要包含以下内容:

    +

    勾起玩家跳跃冲动的互动式玩法

      -
    • Shot selection: 镜头选择
    • -
    • Framing/composition: -电影业通常对镜头的摆放有非常具体的标准以达到特定的情感效果,即使镜头离物体的距离固定,物体在屏幕中的位置也会极大影响呈现的效果,通常遵循三分法则(rule -of thirds)
    • -
    • Transitions: 有几种常见的转场方式: +
    • 宫本茂说:“我认为互动的乐趣之一在于:一个人对自己的某种想法付诸实践之后,能够获得相应的反馈”。
    • +
    • 《超马》为跳跃这一动作准备了大量的反馈。
        -
      • Jump cut: 遵循30度法则
      • -
      • Cross fade or dissolve: 此类转场可以降低渲染性能要求
      • -
      • Iris transition: 以屏幕一点为圆心向四周展开
      • -
      • Wipe(s): 擦拭转场
      • -
      • Viewport transitions: 视口转场
      • +
      • 操作复杂度<动作数<反馈数
      • +
      • 即用较少的动作获得较多的反馈。
    • -
    -
    -Rule of thirds示意。 - -
    -

    Tools

    -

    如果电影镜头使用游戏引擎进行渲染,即用常规游戏环境制作镜头,则最好让编辑器支持直接控制cinematic -sequences,包括:

    +
  • 那么《超马》凭什么让人能够不自由自主地想要按下跳跃键呢?
      -
    • 直接对相机位置和朝向的控制
    • -
    • 通过另一个物体控制相机
    • -
    • 控制相机之间变化
    • -
    • 通过相机触发游戏事件
    • -
    • 镜头的转场
    • -
    • 控制FOV
    • -
    • 即时预览
    • -
    • 额外的编辑和debug工具
    • -
    -

    上述属性都可以通过一条曲线完成(类似Unity),横坐标是elapsed -time,纵坐标是具体值

    -

    Part 2: Design Principles

    -

    Chapter 4: Camera Design

    -

    Interactive 2D Camera -Systems

    -

    2D游戏通常使用tile -map,每个tile都是一个小图片,且使用hash存储,因此极大节省内存

    -

    2D相机的主要功能之一是让角色一直位于屏幕可视范围,比如可以使用scrolling,即让背景做相对运动。下面是一些常见的scrolling类型:

    +
  • 存在让玩家不由自主想要去实践地机制,称其为“游戏的钓饵”。比如玩家用小马里奥顶砖块,获得的反应是砖块向上被拱起这一动画。 +这一动画会在玩家心中放下一个钓饵,让玩家觉得砖块里藏着什么。
  • +
  • 游戏里一定要有引诱玩家付诸实践的钓饵
  • + + +

    从《2D马里奥》到《3D马里奥》

      -
    • Continuous: 背景的scrolling随着游戏连续进行
    • -
    • Character-relative: 相机的运动与角色的运动同步
    • -
    • Directional lag: 期望的相机位置基于角色运动的方向
    • -
    • Burst: 期望的相机位置只有当角色到达某个位置或距离时才变化
    • -
    • Screen-relative: 只有在角色超出预定义的边界时相机才开始移动
    • -
    • Region-based: 当角色超过相对于世界的边界时相机才开始移动
    • +
    • 《3D马里奥》出乎意料地取消了移动动作的惯性,可能是因为3D视角的距离感相比2D要更难以掌握,加入惯性会使操作难度上升。
    • +
    • 《3D马里奥》中镜头角度局限在了“侧面”“上方”“倾斜”三种固定模式中,而不是像一般3D游戏将视角决定权交给玩家。同时,滑垫也被限制在了16个方向以内,让镜头角度与移动角度保持一致,从而帮助玩家找回2D游戏的感觉,同时也避免了因斜向移动导致误判跳跃距离。
    • +
    • 同时,在《超级马里奥3D大陆》中还首次加入了在跳跃过程中改变方向的机制。
    • +
    • 加入了翻滚、远跳、翻滚跳等动作,吸引高端玩家重复挑战关卡。
    -

    有时候相机的位置要独立于角色位置。对共用一个viewport的多人游戏来说,所有角色的位置共同决定了相机的位置。解决方案:

    +

    小结

      -
    • 允许相机zoom out
    • -
    • 限制玩家移动
    • -
    • 分屏
    • -
    • 允许玩家在屏幕外移动
    • -
    • 传送玩家
    • +
    • 要给玩家带来“感官刺激”“风险与回报”“动作与反应”和“连环钓饵”。
    • +
    • 游戏体验的真实性并不仅取决于贴图和声效等外观因素,通过游戏内部机制表现出的“互动的乐趣”也有巨大影响。
    -

    Cinematic 2D Camera Systems

    -

    2D cinamatic camera system的一些典型特征包括:

    +

    让游戏更具临场感的玩家角色动作设计技巧(战神三)

    +

    不需控制镜头的移动操作机制

      -
    • Viewport panning control: 控制运动路径
    • -
    • Zoom in/out: 放大缩小
    • -
    • Object tracking: 目标跟踪
    • -
    • Keep an object within screen bounds or other arbitrary -viewport-based constraint: 带有限制的移动
    • +
    • 战神是TPS游戏,角色的移动方向与遥感倾斜方向一致。也会自动选取最佳角度。
    • +
    • 采用自动镜头的3D动作游戏需要很好地处理镜头移动。 +
        +
      • 可以为每个场景设置不同风格的自动镜头,同时让玩家体验到2D游戏简单而又直观的操作感,以及3D游戏身临其境般的操作感。
      • +
      • 通过自动镜头,战神三创造出了电影般的临场感与魄力。
      • +
    -

    Interactive 3D Camera -Systems

    -

    交互式3D相机系统的难点在于提供一个上下文合适的视角,包括艺术性与游戏性,特征包括:

    +

    实现快节奏战斗的玩家移动动作机制

      -
    • 渲染投影类型(透视、正交)
    • -
    • 决定相机期望位置(navigation)
    • -
    • 相机移动的方式
    • -
    • 决定相机期望朝向
    • -
    • 相机旋转的方式
    • -
    • 相机其他属性的动态改变(FOV, etc)
    • -
    • 相机插值
    • -
    • Fail-safe handling
    • -
    • 多视口/分屏
    • +
    • 要实现快节奏战斗,玩家必须能够灵活转身、迅速静止,因此战神三的静止具有即时性,同时旋转左摇杆可以实现角色的原地旋转。
    • +
    • 《怪猎4》玩家的转身半径要大于战神三,原地回头的时间也略长。 +
        +
      • 怪猎4想创造出“狩猎”的感觉。
      • +
    • +
    • 每个游戏都有一套能让玩家觉得舒服的“速度”(转身速度、移动速度、拔刀速度、收刀速度、硬直速度)“节奏”(具体的动作设计、动作之间的连贯性)和“触感”(动作的打击感)。根据这三点找出合适的玩家角色动作机制,是现代游戏开发的重点。
    -

    Cinematic 3D Camera Systems

    -

    当设计3D cinematic camera system的时候有几个有用的建议:

    +

    不带来烦躁感的地图切换机制

      -
    • Duplicate controls and functionality from modeling packages: -复用现有建模包中的功能,让艺术家能够用得舒服、更有效率
    • -
    • Develop a common terminology: 尽可能用标准的专业术语
    • -
    • Consider pre-rendering cinematic sequences: -用游戏引擎渲染sequence
    • -
    • Distinguish non-interactive sequences from regular game play: -要让玩家意识到在播放cinematic -sequences时不能再操纵角色了,比如调整aspect ratio
    • -
    • Allow an early out after viewing scene once: -让观众有机会跳过cinematic sequence
    • +
    • 战神三在切换地图之后玩家仍然继承之前的移动方向。只要玩家不放开左摇杆方向,就会继续沿着切换前的方向行进。
    • +
    • 3D动作游戏的核心是:让玩家角色的动作准确反映玩家意图
    -

    2.5D Camera Systems

    -

    2.5D相机系统有自身的优势,比如让游戏玩家更容易理解和接受游戏

    -

    Display Devices

    -

    让游戏分辨率适应显示分辨率的方法:

    +

    让人不由得手指发力的玩家角色动作机制

      -
    • Resize: 通过render buffer和viewport匹配显示设备的分辨率
    • -
    • Pan and scan: 裁剪
    • -
    • Stretch: 伸缩
    • +
    • 为烘托角色服务的动作需要由互动动作实现。 +
        +
      • 比如开宝箱这个动作需要玩家长按R1,主角运足全身力量掀开宝箱的动画贯彻玩家按键始终,引得玩家下意识加大按键力道。
      • +
      • 开门同理。
      • +
      • 这种称为互动性演出
      • +
    -

    Camera Design process

    -

    首先,游戏的整体体验/玩法需要制定下来,然后再制定相机的宏观设计框架。相机设计的总体流程是:

    +

    小结

      -
    • Examine high-level design goals: -明确在游戏的不同部分相机有何相同之处,是否需要单个representation -style,玩家有怎样的体验
    • -
    • Evaluate player character abilities: -玩家怎么移动、怎么与环境交互
    • -
    • Determine scope of environments: -游戏场景是怎样的,要提前根据场景做camera prototyping
    • -
    • Define base camera behaviors: 尽快确定相机的基础功能
    • -
    • Determine area-specific requirements: 明确特殊区域
    • -
    • Technical evaluation of cameras: -关注处理器开销、内存开销、脚本能力、Debug能力
    • +
    • 战神三在角色动作上的设计创造玩家与玩家角色融为一体的真实游戏体验。
    -

    在相机设计的时候可以问一些问题,这有助于我们更好地设计:

    +

    让割草游戏更有趣的攻击动作设计技巧(战神三)

    +

    让攻击准确命中目标敌人的机制

      -
    • Player abilities and controls +
    • 战神三并没有锁定操作,实际上,游戏一致在进行锁定和解除锁定,只是玩家没有察觉到而已。
        -
      • 角色怎么移动的?加速度是什么?是否能跳/飞?
      • -
      • 角色的移动能力是否最终确定了?
      • -
      • 相机与玩家的关系是怎样的?
      • -
      • 是否应该保持与玩家朝向相对固定的朝向?
      • -
      • 玩家想要看到什么?
      • -
      • 相机应该关注角色还是角色前的某个点?
      • -
      • 是否有必须可视的部分?
      • -
      • 除了角色之外是否还有其他物体必须可视?
      • -
      • 相机的朝向是否需要改变?
      • -
      • 玩家角色怎样被控制?
      • +
      • 玩家倾斜左摇杆时,玩家角色会锁定移动方向上最近的敌人。
      • +
      • 锁定过程中,角色会一直面朝敌人。
      • +
      • 可以通过左摇杆锁定其他敌人,如果超出一定距离,锁定会解除。
      • +
      • 消灭敌人后,锁定会自动解除,如果还有其他敌人,会继续自动锁定。
    • -
    • Environmental concerns: 场景设计应该围绕玩法和相机限制 +
    +

    让连击畅快淋漓的机制

      -
    • 角色在怎样的场景中移动?开放还是封闭,宽敞还是狭窄?
    • -
    • 场景是否影响相机的位置和朝向?
    • -
    • 相机是否应该被放在场景外?如是,如果在常规玩法中切换?
    • -
    • 相机是否会在动态变化的场景中跟随玩家?受到阻挡怎么办?
    • -
    • 相机是否应当避免复杂物体?
    • -
    • 相机是否应当限制在具体的路径或表面?
    • -
    • 哪种presentation style更好?
    • -
    • 在场景中相机插值是否还生效?
    • -
    • 是否有必要给相机加上特定的碰撞体?
    • -
    -
  • Technical concerns +
  • 普通攻击以灵活为主,一般会在第三击转换为大范围横向攻击。
  • +
  • 重攻击出招慢,以纵向为主。
  • +
  • 开始以普通攻击震慑周围敌人,再以重击给单个敌人予以重创。
  • +
  • 战神三在设计攻击种类和连击招式时,为每一个攻击动作都分配了固定的用途,玩家能够享受制定战术的乐趣。
  • +
  • 每一个招式都是由攻击动画 +、攻击力攻击方向追踪性能等要素组合而成。
      -
    • 是否有技术限制?包括渲染性能问题等
    • -
    • 如果相机会穿透物体,是否支持透明化?
    • -
    • 是否需要fail-safes以处理未预料的意外情况,比如关门?
    • -
    • 是否允许操纵相机?
    • -
    • 场景是否可能遮挡玩家而无法解决?此时场景是否需要进行改变?
    • -
    • 是否支持相机插值?如何避免插值间的遮挡?
    • +
    • 所谓“追踪性能”,就是指玩家角色发动攻击招式时,根据已锁定的敌人所在的位置自动进行追踪的功能。
    • +
    • 普通攻击威力小但追踪性能高,重击威力大但追踪性能低。
    • +
    • 如果一款游戏的连击系统能让玩家觉得畅快淋漓,那么其对追踪的调整一定十分到位。
  • -

    对于相机的要求、限制应该在项目早期就明确,除非是个别例外,并且要避免在项目开展后大改相机系统。因此,要在项目商讨环节把需求都明确清楚

    -

    团队中的camera -designer不仅需要负责相机表现,而且也需要负责大部分情况下的相机解决方案。Lead -camera -designer需要具备美学sense,包括游戏内容的呈现和玩家操作、感知的表现,此外,他还需要推动团队提高认知

    -

    Camera Design Guidelines

    -

    一些相机设计的建议:

    +

    菜鸟也能轻松上手的畅快的浮空连击机制

      -
    • Attempt to keep the player character in view (3rd person -cameras)
    • -
    • Prevent the camera passing through (or close to) game objects or -physical environmental features
    • -
    • Do not require the player to manipulate the camera simply to play -the game -- unless it is a design requirement
    • -
    • Allow camera manupulation when possible or dictated by game design -requirements
    • -
    • Minimize unintentional camera motion whenever possible
    • -
    • Ensure camera motion is smooth
    • -
    • Limit the reorientation speed of the camera
    • -
    • Limited roll should be allowed in most regular game cameras
    • -
    • Do not allow the camera to pass outside the game world
    • -
    • Retain the camera position with respect to the player when instantly -moving th camera to a new position (3rd person cameras)
    • -
    • Do not focus directly on the player character when it is moving
    • -
    • Retain control reference frame after rapid or instantaneous camera -motion
    • -
    • Avoid enclosed spaces with complex geometry (3rd person -cameras)
    • +
    • 割草类游戏都将空中连击的下落设置得比较慢。
    • +
    • 在战神三中,玩家只需长按键就可发动挑空攻击。
    • +
    • 此外,在玩家随挑空攻击后插入了短暂的慢放,相当于一个信号,帮助玩家准确把握发动空中连击的时机。
    -

    Chapter 5: Camera Solutions

    -

    Game Genre Camera Solutions

    +

    用简单操作发动复杂连击的机制

      -
    • FPS游戏:目标是提供沉浸式的体验 +
    • 两个攻击键,短按与长按。
    • +
    • 如果连击第一招需要判断长按还是短按,需要在“长按”判定结束之前,所有受影响的连击都应用同一个攻击动画。
    • +
    • 战神三中的连击只有第一招需要判定按键长短,连击过程不需要。《鬼泣》和《猎天使魔女》则支持连击过程长按。
    • +
    • 战神三还可以在连击过程中切换武器。
    • +
    +

    让玩家角色动作更细腻的设计技巧(《塞尔达传说:天空之剑》)

      -
    • 位置:一般与玩家视角同步,但并不是玩家眼睛的位置,因为武器、手臂等必要元素通常不能从眼睛位置可视
    • -
    • 朝向:通常与人眼可视方式一样,即free-look,有时候可帮助玩家自动调整垂直方向的朝向
    • -
    • 武器或手臂位置:武器与手臂通常可见
    • -
    • 与外部观测的不同:多人游戏中其他玩家看到的角色可能不同
    • -
    • 与三人称之间的切换:常用jump cut实现切换
    • -
    • Zoom effects
    • -
    • Lack of peripheral vision
    • -
    • Aiming position
    • -
    • Motion sickness
    • -
    • Camera shaking
    • -
    • Cmaera bob: 角色运动时相机的轻微移动
    • -
    • Idle wandering
    • -
    • Scale: 人物在高速移动时往往会scale -尽管相机和人物之间保持相对距离,但是相机也对影响人物移动的因素相当敏感,比如在复杂的地形上,角色可能被垂直提高一段距离,这通常不到一秒钟的时间,但是仍然会对相机产生影响,导致glitch现象,这可以通过增加垂直方向的damping解决,这只有在角色穿越崎岖地表时才生效。注意一人称的damping值要比三人称小
    • -
    -
  • Character/action -adventure游戏:可以根据角色的技能决定相机的行为,比如大致分为两类:地面与飞行
  • -
  • Stealth: 相机会随着使用物品或武器而变化
  • -
  • 3D platform: 主要分为两种形式:free form与pre-determined +
  • 将玩家动作与角色为动作融为一体。 ### +支撑海量解密内容的玩家角色移动动作
  • +
  • 按下Z键,镜头会自动调整至林克面朝的方向。
  • +
  • 在悬崖边行走不会跌落。
  • + +

    让玩家下意识选择合适动作的Z注视机制

      -
    • free-form: 相机的位置与距离可以由玩家操控
    • -
    • pre-determined: 严重依赖于特定的Game -play,同时也要兼顾相机的效果
    • -
    -
  • RPG: 常用三人称相机
  • -
  • Scrolling
  • -
  • Sports: TV-style presentation may be very desired +
  • 采用“普通移动”“Z注视移动”“Z注视锁定敌人移动”三种移动模式。
  • + +

    能单独当游戏玩的移动动作——奋力冲刺

      -
    • Single-participants sports: 相机只需要focus on玩家或者单个物体
    • -
    • Two or four-participant sports: 联机or分屏
    • -
    • Team sports: 预留一个control -command标识当前正在控制哪个角色以及哪些角色可以被控制
    • -
    • Court and indoor sports: -对球类运动而言,难点在于追踪及可视球;对桌类运动而言,要提供整个桌面的视野并简化玩家操作
    • -
    • Outdoor sports: -对于场地运动,可以提供能够自主操作的相机;自由运动;轨道运动
    • -
    • Abstract sports
    • -
    -
  • Racing games: -可提供额外的视角以观测其他人的位置,增大FOV可以制造物体快速通过玩家的感觉
  • -
  • Ground vehicles
  • -
  • RTS
  • -
  • Flight simulation: 一个重要的方面是是否允许相机随着飞机一起roll +
  • 冲刺消耗耐力值,设置了只有奋力冲刺才能通过的斜坡。
  • +
  • 翻滚冲刺、沿墙冲刺等等。
  • + +

    没有跳跃键却可以体验真实跳跃的机制

    +
      +
    • 自动触发跳跃,但并没有失去跳跃的乐趣。玩家依然需要掌控距离,需要助跑等等。
    • +
    • 自动跳跃可以避免空气墙。
    • +
    +

    头脑与身体一起享受的剑战动作设计技巧(《塞尔达传说:天空之剑》)

    +

    能帅气挥剑的机制

      -
    • Realistic flight models: 此类游戏通常从驾驶舱或飞机后侧观察
    • -
    • Non-realistic flight models
    • -
    • Artificial horizon: 可以用仪器监控飞机的pitch或banking
    • -
    • Sensation of motion: -可以用云层或航迹云增强飞机的速度感;航天器可以用小的残骸增强速度感;也可以通过音效、FOV等方式
    • -
    -
  • Adventure: present a very cinematic experience to the player
  • -
  • Puzzle/party games/board games
  • -
  • Fighting/close combat: -场景一般很简单以便于相机摆放,需要一些相机移动突出角色的操作
  • +
  • 要将遥控器当成真剑来操作,带来了前所未有的战斗动作享受。
  • +
  • Wii将人的动作符号化,让普通玩家也能享受到乐趣。
  • -

    Multi-Player Camera -Solutions

    -

    多人共享屏幕时,相机设计会有很大难度,有时候可以通过alternate -players解决,但多数情况下并不适用。一般来说可以有两种解决方法:

    +

    攻击与体力的机制

      -
    • 分屏
    • -
    • 单屏包含所有玩家
    • +
    • 游戏系统设定的连击结束时攻击即结束,最后一招往往动作较大、破绽较多。
    • +
    • 为玩家设置体力参数,归零时攻击结束。
    • +
    • 玩家没有体力或者没有挥动Wii时,攻击结束。
    • +
    • 一般动作游戏是A与B结合,而天空之剑是B与C组合。
    -

    下面是对相关技术的介绍:

    +

    让玩家痛快反击的盾击机制

      -
    • Single-screen techniques: 格斗游戏、合作游戏中常见 +
    • 盾击会让敌人暂时失去平衡,创造反击的机会。
    • +
    • 盾击是通过“玩家盾击动作”与“敌人攻击动作”中设置的“盾击成功判定帧”进行判定的。如果这两个动作中的“盾击成功帧”重合,则判定盾击成功。
    • +
    • 盾牌加入耐久。
    • +
    +

    实现剑战动作的机制

      -
    • 相机的位置很难确定
    • -
    • 通常相机位置固定在一个离游戏世界表面相对的高度,而与玩家移动无关
    • -
    • 相机位置可能导致朝向的迅速变化
    • -
    -
  • Split-screen techniques +
  • 当面对多个敌人时,只集中精力与一名敌人对战,基本上是一对一。
  • +
  • 通过Z注视机制实现剑战。受到Z注视的敌人会先积极攻击。
  • + +

    剑战动作与割草游戏的区别

      -
    • 每个玩家的分屏区域一样大
    • -
    • 根据活跃玩家动态分屏
    • -
    • 为了提升性能,尽量让所有玩家都处于同一个环境中
    • -
    • 尽量减少玩家获取的信息
    • -
    • 避免把重要的物体放在设备屏幕边缘
    • -
    -
  • Transitioning from full-screen to split-screen +
  • 战斗中的跳跃不同。塞尔达采用了自动跳跃,不会出现跳跃闪避攻击的情况。战神则可以通过跳跃躲避敌人。
  • +
  • 割草游戏有大量的AOE,塞尔达大多为单体攻击。
  • + +

    完美演绎英雄的玩家角色动作设计技巧(《蝙蝠侠:阿甘之城》)

    +

    能像蝙蝠一样在三维空间自如穿梭的机制

      -
    • 保持相机相对距离相同
    • -
    • UI元素要重新排布
    • -
    • FOV要根据aspect ratio重新调整
    • -
    -
  • Transitioning from split-screen to full-screen +
  • 滑翔与抓钩就是“平面+高度”的移动手段,还有其他作用。
  • +
  • 在大楼之间移动时,玩家只需要按住X操作左摇杆,就能根据间隔和高度进行跳跃或自动切换至滑翔状态。不会因为跳跃失误而遭失败。
  • +
  • 抓钩也实现了精准操作和粗略操作。
  • + +

    通过简单操作实现高自由度的玩家角色动作的机制

      -
    • 玩家移动地足够近的时候
    • -
    • UI元素要等transition完成时再移动
    • -
    • 相机相对位置保持不变
    • -
    • FOV调整
    • -
    +
  • 玩家角色的移动动作就有很多种,移动动作并不由玩家的手柄决定,而是由玩家触碰的物体决定。(类似刺客信条)
  • +
  • 蝙蝠侠将移动动作特有的难点——时机把握从基本移动操作中剔除,是一款专注功能可供性的动作游戏。
  • -

    Gamera Scripting

    -

    What is Meant by Scripting

    -

    定义与控制物体加偶和的过程被称为scripting,在游戏中,指控制游戏事件发生的事件与物体之间的交互。Scripting允许设计师在没有程序员介入的情况下完成迭代

    -

    Types of Scripting

    -

    我们希望构建一个与游戏类型无关的scripting系统,主要由两个方面组成:scripting -language和event messaging

    -

    Scripting languages

    +

    演绎一名不会轻易死亡的英雄

      -
    • Text-based scripting languages: -包括pre-compiled和interpreted两类
    • -
    • General-purpose programming languages: 编程语言
    • -
    • Custom scripting languages
    • -
    • Finite state machines
    • -
    • Visual scripting languages
    • +
    • 在奔跑中碰到墙壁,移动动画会终止。
    • +
    • 在滑翔时碰到墙壁,蝙蝠侠也不会落下去,而是抓住墙壁。
    -

    Event messaging

    -

    Event -messaging要求定义不同物体之间的关系。典型的能够发送消息的事件包括:

    +

    让玩家化身为英雄的设计技巧(《蝙蝠侠:阿甘之城》)

    +

    让战术自由度更高的机制

      -
    • 关卡开始
    • -
    • 物体加载
    • -
    • 游戏暂停
    • -
    • 进入或退出volume
    • -
    • 玩家受到伤害
    • -
    • 物体到达路径终点
    • -
    • 玩家状态改变
    • +
    • 潜行动作游戏在游戏中的流程分为“侦察”“制定战术”“捕食战术”“格斗战术”四个阶段。
    • +
    • 蝙蝠侠在捕食战斗阶段能削减敌人多少就成了战斗取胜的关键。
    • +
    • 蝙蝠侠提供了50多种不同的动作,具有极高的自由度。
    -

    能够被事件发送的典型消息包括:

    +

    让人忍不住要尝试的工具机制

      -
    • Start
    • -
    • Stop
    • -
    • Activate object
    • -
    • Delete object
    • -
    • Query state
    • +
    • 蝙蝠侠中有很多工具,或者道具,一共有20多种。这些工具不会击溃敌人,只会造成负面效果,保证了工具不会打破战斗平衡性。
    • +
    • 工具和战斗动作可以组合,使得产生“有趣的反应”和“新动作”
    -

    Script Objects

    -

    用于定义物体的属性和行为

    -

    Scriptable game hints

    -

    Game hints are script objects that provide one possible type of -runtime mechanism by which designers can override player properties, -game controls or camera properties according to events

    +

    让玩家完美演绎蝙蝠侠的捕食者动作的机制

      -
    • Camera hints: 改变相机的属性或移动,可以被重载的相机属性包括: +
    • “无声压制”和“转角隐蔽压制”可以无声无息地击溃敌人,所以玩家能够立刻开始下一步动作而不被敌人发现。
    • +
    • “粉碎重击”和“边缘压制”能产生很大的声响。
    • +
    • 利用有利位置,可以将敌人吊起来剥夺其行动力的“倒吊压制”。
    • +
    +

    改变动作游戏定式的自由流程格斗机制

      -
    • 相机行为
    • -
    • 位置朝向
    • -
    • 相对距离
    • -
    • 看向点
    • -
    • 移动速度
    • -
    • FOV
    • -
    -
  • Player hints +
  • 在一般的动作游戏种,玩家需要根据周围敌人的情况选择固定的攻击动作或连击,然后输入指令来发动。
      -
    • Prevent motion of the player
    • -
    • Relocate player position
    • -
    • Change player movement characteristics
    • -
    • Flags to indicate special kinds of game play
    • +
    • 要先考虑与敌人的距离、招式的速度、招式的追踪性能、招式的属性。
  • -
  • Control hints +
  • 自由格斗,玩家输入的不是攻击招式,而是适合周围状况的攻击动作。
  • +
  • 一个按键对应多个攻击招式以及敌人反应。
  • +
  • 蝙蝠侠仍然是一款根据环境自动选择动作的游戏。
  • + +

    还原机器人动画的玩家角色动作设计技巧(《终极地带:引导亡灵之神》)

    +]]>
    + + 游戏 - 游戏理论 + + + 随笔 + 游戏 + +
    + + 一道有趣的概率题 + /2022/10/20/23/59/ + 独立同分布,令,求。换句话说,我们要求最先使得若干个独立同分布于的随机变量之和大于的期望。

    + +

    问题定义

    +

    这个问题的定义已经定义在上面了,这里再复述一遍:

    +

    独立同分布,令,求

    +

    引入函数

    +

    乍一看这个题似乎无从下手,但是我们可以发现这里这个条件似乎可以换成任意一个,这启发我们用一个函数去表示我们要求的式子,然后通过求解一个“递推式”(实际上是一个微分方程)解出这个函数,进而得到某个具体点的值。

    +

    从这个思路出发,我们不妨定义,进而令。显然,我们的目标就是求

    +

    求解方程

    +

    那么,怎么求出呢?注意题目中的条件独立同分布,因此我们可以把里面的拆出来,变成:

    +

    +

    为了简便起见,我们不妨限定。此时考虑两种情况:

    +

    如果,那么就等于,所以这等价

    +

    如果,那么就相当于后面的,再由独立同分布知道这就是,但这是以为条件的,所以实际上还要对求个积分,也就是:

    +

    +

    把上面两个加起来,就有:

    +

    +

    进而得到微分方程及其初始值:

    +

    +

    很容易求解得到,所以我们就能得到最终的答案

    +]]>
    + + 数学 - 概率论 + + + 数学 + 随笔 + 概率论 + +
    + + 你为什么不笑了 + /2022/10/29/00/35/ + 你为什么不笑了?

    + +

    奶奶,你为什么不笑了?
    +还记得,一年前的你,
    +走在春风里,
    +向着山顶慢慢登去。
    +你摘下路边的一朵浅梅,
    +捧在手心。
    +阳光洒下,
    +手中的梅花,也映衬出了你脸上的笑容。

    +

    你的儿子女儿挽着你的手,
    +一步一步向山顶走去,
    +你看见一棵古树,
    +你说这是拍照好去处。
    +女儿扶着你,
    +你扶着树,
    +阳光洒下,
    +树上的绿叶,也甘愿陪衬你脸上的笑容。

    +

    来到山顶,
    +黄花遍地,
    +你开心得像个孩子,
    +伸出双手,
    +比了两个大大的耶,
    +那时的你,真的好美。

    +

    奶奶,从什么时候开始,你不笑了?
    +从拔掉爷爷呼吸机的那个夜晚开始,
    +你似乎一直睡得不好。
    +你还能梦见吗,
    +另一张空荡荡的床上,
    +曾经那个老人的模样?
    +从前他是那样健康,
    +如今却骨瘦嶙峋,
    +让时间风干了皮肤,
    +侵蚀了思想。
    +你还能看见吗,
    +那个明媚下午的阳光,
    +案前那个老人伏着阅读着,报刊也泛黄。

    +

    奶奶,你为什么不笑了?
    +近日来,
    +你总是抱怨,
    +胸口有恙。
    +你变得啰嗦、唠叨,
    +总是对小事斤斤计量。
    +我好想你能多笑笑,
    +放下生活的疲劳,
    +忘却心中的郁结,
    +再回忆,
    +我们一起出游时,
    +小鸟的啼叫,
    +鲜花的绽放,
    +阳光的照耀,
    +我们手拉着手,
    +在田园中,聊些家常,传来欢笑。

    +

    奶奶,时间真的很残酷,
    +也许当我走时,
    +这个世界便再无你的回响,
    +人生人寂,人来人往,
    +至我去时,此情已了,略无痕迹。

    +

    奶奶,再让我看看你的笑,
    +那个阳光中,
    +像孩子般最天真的笑容。

    +]]>
    + + 随笔 + + + 随笔 + 生活 + +
    + + 《Real-Time Cameras》笔记 + /2022/05/05/21/40/ + 本文是《Real-Time Cameras: A Guide for Game Designers and +Developers》的阅读笔记,放在此备份供参阅。

    + +

    Part 1: Core Concepts

      -
    • Disable specific controls or sets of player controls
    • -
    • Specify the control reference time and the time to interpolate to -that new reference time
    • -
    +
  • 第一章:介绍了游戏应用与其中的相机系统。
  • +
  • 第二章:讲述游戏相机的基础概念与发展。
  • +
  • 第三章:介绍电影摄影的基础知识与应用。
  • -

    很多时候事件需要有先后顺序,一个sequenced event -manager可以用来定义事件的先后顺序。

    -

    Camera Scripting

    -

    Camera scripting methods

    +

    Chapter 1: Game Overview

    +

    一个典型的游戏引擎(Game Engine)应当包括:

      -
    • Pre-defined camera scripting
    • -
    • Dynamic camera scripting: 只调整需要的相机属性
    • +
    • 资源管理系统(Resource Management)
    • +
    • 玩家控制(Player Control)
    • +
    • 物理模拟(Physics Simulation)
    • +
    • 游戏对象逻辑(Game Object Logic)
    • +
    • 图形用户界面(Graphical User Interface)
    • +
    • 相机与视口管理(Camera and Viewport Management)
    • +
    • 网络交流(Network Communication)
    • +
    • 人工智能(AI)
    • +
    • 游戏事件管理(Game Event Management)
    • +
    • 渲染(Rendering)
    • +
    • 对象间交流(Inter-Object Communication)
      +
    • +
    • 音频(Audio)
    • +
    • 任务管理(Task Management)
    • +
    • ……
    -

    DCamera control

    +
    +游戏各系统之间的联系。 + +
    +

    游戏的每个系统都有相对固定的更新顺序,如下图所示:

    +
    +游戏各系统的更新顺序。 + +
    +

    对于每个步骤的介绍如下:

      -
    • Third person scripting: 用专门的script objects去调整FOV、Focal -Length,filter or rendering effects, fog distance等
    • -
    • First person scripting: 有一些情况也需要对FP相机进行动态控制,比如 +
    • Input:通常是每帧更新一次,但一些游戏也会每帧更新若干次,从而精准反应玩家输入。
    • +
    • Think:大部分游戏逻辑得到更新,包括玩家输入、AI、游戏事件生成等。
    • +
    • Move:更新物理模拟、碰撞检测等内容。
    • +
    • Messaging:用于在游戏对象、游戏系统之间传递信息,比如新游戏对象的激活、开门、敌人死亡、资源加载完成、动画事件、碰撞等等。Messaging并不是一定会在Update +Loop中出现,可能会在消息发出的瞬间被处理,或者延迟处理。Messaging系统有利于不同系统之间的解耦,简化游戏录像逻辑。Messaging本质上是一种保证不同时间点游戏事件同步的技术。
    • +
    • Camera:主要包含以下步骤:
        -
      • Traversing up or down ramps or other significantly sloped surfaces: -在上下坡的时候线相机要和坡度一致 根据玩家位置自动pitch。
      • -
      • Jumping: 在高距离跳跃时,能够对准落脚点的方向,但幅度不宜过大 玩家跳跃时自动pitch。
      • -
      • View locking: 锁定目标时相机要朝向目标点 玩家锁定目标。
      • +
      • 移除无用的camera
      • +
      • 激活需要的camera
      • +
      • 决定哪个camera用于渲染
      • +
      • 更新当前激活的相机
      • +
      • 更新主相机
      • +
      • 基于主相机更新玩家control reference frame
      • +
      • 构建transformation matrices用于渲染,包括特效如shaking, FOV等
    • -
    • Non-interactive movie scripting: -最常用的就是位置和朝向的scripting
    • -
    -

    一些经验法则:

    -
      -
    • Apply Occam's Razor: 使用最简单scripting方案,这便于随时修改
    • -
    • Allow for different ways that the camera event may be triggered: -比如有时候只用trigger volume可能不够
    • -
    • Allow previewing of camera motion and orientation: 支持预览
    • -
    • Assess camera scripting requirements early in the design -process
    • -
    • Make the scripting development cycle as efficient as possible: -提高预览和扩展能力
    • -
    • Mimic the behaviors of the tools used by artists where possible: -降低艺术家(设计师)的学习成本
    • -
    • Allow for game play changes: camera scripting system: -游戏玩法改变时能够轻松地修改camera
    • -
    -

    Scripting Tools

    -

    World editor support

    -

    必须支持script objects的放置与操作,而且还要支持message passing, -state transition, event trigger和其他物体间的关系

    -

    Communication -between target platform and development PC

    -

    要获得运行物体的状态

    -

    Messaging/event logging

    -

    对消息和事件进行记录

    -

    Object properties debugging

    -

    当游戏运行时要动态检查物体的属性

    -

    Replay

    -

    最简单的方法是内置video recording

    -

    Console window

    -

    支持打印文本消息,通过GM指令查看想要的文本消息

    -

    Scripting Debugging

    -

    常见的debugging方法:

    -
      -
    • Script statement execution/filter: -对任意给定的script,展示每个状态执行的历史记录
    • -
    • Debug message logging: 单独展示日志
    • -
    • Message filtering: 增加过滤功能以更好地debug
    • -
    • Object state: 展示物体的状态
    • -
    • FSM state changes: 展示当前的state和历史state
    • -
    • Animation state and event management: -记录当前的动画状态和插值状态,记录事件
    • -
    -

    Part 3: Camera Engineering

    -

    Chapter 7: Position and -Orientation

    -

    Coordinate Schemes

    -

    有几种不同的坐标空间:

    -
      -
    • World Space: 从世界空间到相机空间再到屏幕空间
    • -
    • Camera Space: 以相机为原点
    • -
    • Screen Space: Camera Space通过正交变换而来
    • -
    • Local Space: 以角色为原点
    • -
    • Object Relative: 相机为原点,三个轴根据某个物体到相机的位置而定
    • -
    -

    Desired Position

    -

    在相机移动的过程中需要关注与目标点的距离

    -

    First person camera -positioning

    -

    大多数情况下,相机的desired -position就是角色眼睛的位置,此时增加一点dvertical -amping会有用。在玩家遇到崎岖地形或与物体碰撞时,需要smooth -out轻微的相机运动

    -

    Third person camera -positioning

    -

    有三种策略决定相机位置:

    -
      -
    • Automated: 相机完全自动化
    • -
    • Player control: 交由玩家操作相机,但要让环境始终保持在视野中
    • -
    • Hybrid: 交给玩家部分自主权
    • -
    -

    Desired Position -Determination Methods

    -

    相机位置应该独立于朝向,尽管后者常常依赖于前者。下面是一些决定desired -position的方法

    -

    Stationary

    -

    主要有两种stationary cameras: fixed position 和 dynamic fixed -position:

    -
      -
    • Fixed position: -相机位置固定但可以自由调整朝向,通常限制了朝向的范围。但此类相机会使目标离相机太远或者丢失视野
    • -
    • Dynamic fixed position: 通常由其他活跃的相机决定,中间由jump -cut或interpolation决定
    • -
    -

    Slaved/tracking

    -

    相机的offset可以采取以下的几种形式:

    -
      -
    • World-relative offset: 在世界坐标内保持相对距离
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    • World-relative angular offset: 在世界坐标中通过角度定义相对距离 -
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      // construct a rotation matrix around the world up-axis
      Mat3 rotation = Mat3::ZRotation(yaw);
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    • World object relative: -根据主物体和另一个物体之间的距离决定相机位置,这种方法可以帮助处理人物在相机和另一物体中间的情况 -
      Mat3 rotation = Mat3::LookAt(GetTargetObject()->GetPosition().DropZ(), mWorldPosition.DropZ());
      // note that the reference frame is often 2D, but is not required to be
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    • Local offset: 与world relative -offset类似,只不过offset被转换到了局部空间
      Mat3 rotation = mScriptObject->GetMatrix(); // based on the orientation of the script object
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    • Local angualar offset: 类似world-relative angular -offset,不过offset被转换到了局部空间
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      Mat3 rotation = mScriptObject->GetMatrix();
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    • Character-relative offset: -在局部坐标内根据角色的朝向决定相机的位置,在三人称相机中最为常用,被称为“追背相机 -(chase camera)”, 一般不会变化Roll
    • -
    • Character-relative angular offset: -在局部坐标内根据角色朝向和三个旋转角度、距离决定相机位置,通常会限制相机旋转速度的变化率
    • -
    • Object-relative offset: -不考虑目前物体的朝向,而仅考虑elevation和distance,the vector from the -target object toward the current camera position defines the coordinate -space used to calculate the desired position,相机的朝向通常由玩家操纵 -
      Mat3 rotation = Mat3::LookAt(GetPosition().DropZ(), GetTargetObject()->GetPosition().DropZ(), mWorldPosition);
      // only elevation (here pitch) and distance are required
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • -
    -

    Path

    -

    有几种不同类型的路径:

    -
      -
    • Linear: 直线路径
    • -
    • Circular/elliptical/spiral: 环状路径
      X = a * cos(theta);
      Y = b * sin(theta);
    • -
    • Spline: 通常一系列control points定义
    • -
    -

    下面是决定路径位置的方法:

    -
      -
    • Non-interactive situations: 在movie -sequences中通常由一个时间到位置的映射决定
    • -
    • The position of the player character relative to another object
    • -
    • Player position relative to a defined path
    • -
    • A specified distance away from the closest position on the path to -the player: 改方法需要注意相机移动的平滑性
    • +
    • Post-Camera:更新依赖于相机的物体
    • +
    • Render:有时候一个camera view也会被用于另一个camera +view的生成过程中
    -

    Splines. 可以用brute -force的方法去计算spline的长度,代码如下

    float length(0.0f);
    for (int i = 0; i < controlPoints.Size(); ++i)
    {
    Vec3 pathPosition = EvaluateSegment(i, 0.0f);
    // start of i'th segment
    controlPoints[i].mLength = length;
    // save length at the control point
    for (int j = 1; i < kMaxSegmentSlices; ++j)
    // includes next control point
    {
    Vec3 newPosition = EvaluateSegment(i, j/kMaxSegmentSlices);
    Vec3 delta = newPosition - pathPosition;
    length += delta.Magnitude();
    // add the "length" of this slice
    pathPosition = newPosition;
    }
    } // length now holds the approximate total length

    -

    一种更快且准确的方法是用多项式逼近

    -

    Mapping functions. -最简单的是从时间到长度的线性映射,此时相机的速度不变,导致abrupt开始或结束。一种常见的方法是使用一个二维spline曲线(Hermite -curves),用户可以定义控制点和切线控制曲线的形状

    -

    Surface constrained

    -

    在一些情况下,相机可能限制在一个隐式的平面上,比如一个flat平面,一个椭球、圆柱等等,该平面的大小和朝向可能是动态的

    -

    下面是一些常见的surface类型:

    +

    对于相机而言,Occam's +Razor法则适用:最简单的相机解决方案通常是最好的。

    +

    相机系统的作用包括:

      -
    • Sphere: 容易受gimbal lock影响
    • -
    • Plane
    • -
    • Cylinder
    • -
    • Cone
    • -
    • Extruded spline plane
    • -
    • Spline cylinder
    • +
    • 管理活跃相机,保证其逻辑正常
    • +
    • 控制相机增删
    • +
    • 支持相机参数动态调整
    • +
    • 为玩家操控提供参考帧(reference frames)
    • +
    • 处理所有非交互式电影片段
    • +
    • 管理并更新视口
    • +
    • 获取frustum信息以展示view
    • +
    • 提供可选的debug能力
    -

    Volume constrained

    -

    相机与圆柱体的碰撞检测:

    -
    Vec3 direction = currentPosition - cylinder.GetPosition();
    direction.SetZ(0.0f);
    float radius = direction.Magnitude();
    if (radius > kMaxRadius)
    radius = kMaxRadius;
    direction = direction.AsNormalized() * radius;
    Vec3 newPosition = cylinder.GetPosition() + direction;
    newPosition.SetZ(currentPosition.GetZ());
    -

    Framing

    -

    该方法在屏幕空间上保持目标角色的位置,这可以通过移动相机、修改FOV、伸缩viewport等方法实现

    -

    Object-framing relative

    -

    该方法不仅考虑物体在屏幕空间中的小,而且也考虑它和另一个物体之间的相对距离,一些因素包括:

    +

    角色控制与角色移动之间的关系被称为控制参考帧(control +reference frame)

    +

    Chapter 2: Camera +Fundamentals

    +

    Real-World Cameras

    +

    真实世界的相机大概包含以下关键要素:

      -
    • Object screen size
    • -
    • Multiple objects must be kept on screen
    • -
    • Distance between objects
    • -
    • Relative position to other objects
    • -
    • Relative position to the screen (not rendered size)
    • -
    • Orientation with respect to the camera
    • -
    • Distance aiming/ranged weapons
    • +
    • Lens type
    • +
    • Capture method
    • +
    • Film stock
    • +
    • Capture rate
    • +
    • Exposure of film
    • +
    • Projection method
    • +
    • Aspect ratio
    -

    Axis rotational/spindle

    -

    相机围绕游戏世界中的一个轴旋转、平移,适用于玩家需要围着一个物体导航的情况,或限制在一个圆柱物体的情况

    -

    一些影响相机位置的因素:

    +

    真实世界相机的一些特点:

      -
    • Angular offset relative to the reference vector or the axis -origin
    • -
    • Axial offset relative to the current player positionn or the axis -origin
    • -
    • Radial offset relative to the spindle axis or current player -radius
    • +
    • 缺乏交互性
    • +
    • 预先决定的机位
    • +
    • 相机镜头
    • +
    • 场景切换
    • +
    • 胶片曝光
    • +
    • 后处理特效
    -

    此外,我们也可以改变相机的朝向:

    +

    游戏相机的一些特点:

      -
    • Look at the spindle axis along a projected vector from the current -camera position
    • -
    • Look away from the spindle along a projected vector that passes -through the camera position as above
    • -
    • Apply an angular offset either toward or away from the spindle -relative to the target object
    • +
    • 模拟真实世界相机的功能
    • +
    • 不需要相机实体
    • +
    • 动态变化
    • +
    • 允许游戏世界的不同投影
    • +
    • 可变的光照与渲染
    • +
    • 更多样的转场
    • +
    • 允许特殊效果
    -

    玩家位置相对于spindle axis的半径可以作为mapping -function的输入,当玩家移动半径更小时,相机移动得更高

    -

    Common Position Problems

    +

    一些专有名词:

      -
    • 期望位置和一个物体碰撞,或者太近以至于物体穿透相机的近平面
    • -
    • 期望位置离环境中的边界物体太近以至于有环境外的视野
    • -
    • 期望位置离目标对象太远,玩家没有好的视野
    • -
    • 环境复杂相机不能提供好的视野
    • +
    • Game system: +管理相机、应用相机、控制相机行为、生成用于渲染的信息、控制参考帧
    • +
    • Game camera: 抽象的游戏实体
    • +
    • Presentation style: 指将游戏世界投影到屏幕空间的方法
    • +
    • Camera behavior: 一人称/三人称相机,影院式/交互式相机
    • +
    • Look-at position: 相机朝向的位置
    • +
    • Desired position: 相机移动的位置
    • +
    • Orientation: 相机朝向
    • +
    • Desired orientation: 相机的目标朝向
    • +
    • Rotation: 朝向的变化量
    • +
    • View frustum: 游戏世界的可视空间
    • +
    • Viewport: 呈现在显示设备上的一种数据结构
    • +
    • Field of view: View frustum的上下/左右边界的夹角
    • +
    • Aspect ratio: 长宽比,对于standard definition television +(SDTV)来说,是4:3,对high-definition television +(HDTV)是16:9。对电影投影中使用最多的是1.85 (CinemaScope)和2.35 +(anamorphic)
    • +
    • Refresh or frame rate: 刷新率
    • +
    • Refresh and motion blur: +刷新模糊指显示设备刷新率不能与计算机的输出同步,运动模糊是因为物体运动快于曝光时间
    • +
    • Display tearing: 显示撕裂
    • +
    • Player character: 玩家在游戏中控制的物体
    • +
    • Target object: 被看向的物体
    • +
    • Interpolation: 插值
    • +
    • Projection: 投影
    • +
    • Parallax: 视差
    • +
    • Transitions: +转场,包括过程化转换、插值、瞬时切换、清除、过渡等等
    • +
    • Camera constraints: 相机约束
    • +
    • Motion constraints: +运动约束,如距离、相对位置、路径、速度、加速度等等
    • +
    • Orientation constraints: 通常用于第一人称相机
    -

    Orientation

    -

    旋转有四种表示方法:欧拉角、变换矩阵、轴角和四元数

    -

    Desired Orientation -Determination Methods

    -

    Constant orientation

    -

    朝向不变,但是位置可以移动。可以变式为constant elevation -cameras,即只有pitch可以改变

    -

    这种相机可以用在:

    +

    Camera Presentation Styles

    +

    Presentation style +通常被划分为正交(2D)或者透视(3D)渲染,有时也会使用2.5D

    +

    Camera Behaviors

    +

    主要有三种相机行为:

      -
    • Player controlled remote cameras
    • -
    • Dynamic positioning of replay cameras
    • -
    • Dynamic positioning of caemras for non-interactive game play +
    • Cinematic cameras: +非交互的、玩家不可控的相机,包括cutscene和real-time cinematic sequences
    • +
    • First person cameras: +视野比TP游戏小,使玩家难以全面捕捉空间与环境信息,跳跃也更加困难。在实现FP相机时,建议把相机与任务的头部分开,同时使相机的朝向独立于玩家的朝向
    • +
    • Third person cameras: +允许更大的视野,适用于环境导向型游戏,但存在world navigation与collision +avoidance两个问题
    -

    Tracking a target object -or position

    -

    相机达到期望朝向的方法取决于目标物体的移动和相机的重朝向速度,此类相机一般保持固定的pitch

    -

    Look-at offset

    -

    许多三人称游戏使用一个固定的look-at offset

    -

    Locked look-at position

    -

    玩家的朝向也会随便改变

    -

    Target object position -prediction

    -

    Object framing

    -

    一个很好的例子是格斗游戏,双方玩家都希望自己和对方都能呈现在屏幕上,当玩家距离增加的时候,相机要么拉远,要么增大FOV,但是频繁切换FOV通常不可取

    -

    回放相机通常会从不同的视角呈现玩家行为

    -

    Idle wandering

    -

    A semi-random camera reorientation while the player character is -idle

    -

    Automated orientation -control

    -

    在没有玩家操作的情况下自动帮助相机转向:

    +

    此外,相机也可以分为predictive和reactive两类: - Reactive: +根据游戏对象的变化而变化 - Predictive: +根据当前游戏物体预测最佳的相机位置和朝向

    +

    Path finding solutions: - +全局路径搜索:搜索环境中的一条路径,能够保持美学质量 - +局部路径搜索:以某种具体的物体类型为核心进行搜索 - +基于目标的搜索:在各种约束下搜索路径

    +

    View Generation

    +

    视图生成步骤:

      -
    • Automated control over camera pitch when the player is jumping: -从玩家起跳时开始相机可以向下看
    • -
    • Automated pitch control when traversing environmental features: This -is applied to present a view facing up or down a ramp, staircase or -other incline as appropriate so that players have a better view of what -they are moving toward, -在有洞穴或悬崖的地方,相机应该自动看向以给予提示
    • -
    • Automated pitch control during combat or interactions: -自动调整pitch以提示可交互的物体
    • -
    • Automated reorientation of the player or a camera toward a target -position: 锁定到目标物体
    • -
    • Repositioning and reorientation of the camera to face the same -direction as the player character
    • -
    • Transitions from first to third person cameras: -要保证当相机移动地充分远的时候才渲染物体,避免穿帮,此时可通过fade角色解决此问题
    • -
    • Transitions from third to first person cameras: 可通过cut -transition实现
    • +
    • 游戏中每个活跃的物体更新内部状态
    • +
    • 相机更新位置、朝向等信息
    • +
    • 对游戏中待生成的每个view: +
        +
      • 决定相机的位置和朝向
      • +
      • 决定显示设备上的viewport
      • +
      • 决定游戏世界中的frustum
      • +
      • 给定frustum,决定哪些物体被剔除
      • +
      • 对每个待渲染的物体: +
          +
        • 变换物体
        • +
        • 应用光照着色
        • +
        • 2D投影
        • +
        • 光栅化
        • +
      • +
    -

    Reorientation Methods

    -

    Applying rotations

    -

    有几种方法去应用旋转:

    +
    +简化的渲染流程。 + +
    +

    在上述步骤中,与相机系统直接相关的有:view frustum, view +transform和projection transform

    +

    Player Controls

    +

    A control reference frame refers to a relationship between changes to +the controller input and how it affects movement or other aspects of +player control.

    +

    在FP游戏中,control reference +frame直接对应了玩家角色面朝的方向,且通常只能绕up +axis旋转。在TP游戏中,control reference +frame基于相机与玩家的相对位置。

    +

    Control reference frame需要始终反应玩家的意图

    +

    Chapter 3: Cinematography

    +

    Nomenclature

    +

    电影学中的一些专有名词:

      -
    • Constant angular velocity: -匀速变动旋转角,但是开始和结束会有突变
    • -
    • Acceleration and deceleration: 应用加速度与减速度
    • -
    • Angular velocity damping: 用damp使开始结尾更皮规划
    • -
    • Free-look damping: 用bump和其他的ease -functions防止因noise导致的相机朝向变动
    • -
    • Twist reduction
    • +
    • Dolly: 指平行于地面的移动,dollying in是向前推进,dollying +out是向后推进,crab left是向左移动,crab right是向右移动
    • +
    • Pan/Yaw: 绕着camera up轴的旋转
    • +
    • Tilt/Pitch: 绕着camera right轴的旋转
    • +
    • Tracking: 跟着物体移动
    • +
    • Depth of field: 景深
    • +
    • Color fade: 颜色渐变
    • +
    • Lens flare: 镜头眩光
    • +
    • Cut-away shot: 切换到另一个镜头用于强调原场景中的某个元素
    • +
    • Insert shot: 切换到场景中的细节部分,比如人物特写
    • +
    • Jump cut: 瞬间切换
    • +
    • 180 degree rule/line of action: 二人镜头应该让相机保持在同一侧
    • +
    • Point of view shot: 第一视角镜头
    • +
    • Crane shot: 第三人称视角
    • +
    • Reaction shot: 拍摄人物反应的镜头
    • +
    • Reverse shot: 与前一个镜头角度相反,但仍遵循180度法则
    • +
    • 30 degree rule: 当使用jump +cut时,新镜头与上个镜头的角度差应该大于30度,否则会让观众认为是场景的物体发生了移动而非相机
    • +
    • Forced perspective: +强制透视指一系列技术让观众产生错误的视觉效果,如大小、位置关系
    -

    Reorientation lag

    -

    通常我们希望相机朝向的变化能有一些延迟(lag),同时保持平滑

    -

    很多游戏没有任何延迟,这有两个影响:第一,相机直接看向角色会导致玩家不能提前看见角色的移动,第二,相机非常依赖角色的移动,任何小的扰动就会影响相机表现

    -

    因此需要使小的移动没有影响,并且使用damp。当插值的时候,可以限制每帧允许的旋转角度。此外,lag还可以通过用springs或feedback -controller实现

    -

    Offsets

    -

    一个常被忽略的点是look-at position和target -object之间的关系。如果直接看向角色,那么就容易丢失玩家想要看的东西。可以用一些视觉提示帮助玩家瞄准目标对象,比如highlight敌人,或者lock-on -to the target object

    -

    Smoothing and damping

    -

    常用的平滑方法是limit the angular velocity of the camera -proportionally to the angle between the current and desired -orientations,这可以表示为一个简单的三角函数:

    -
    Real32 const kDampingAngle(30.0f * gkRadiansPerDegree);
    Real32 angle = acosf(CVector3f::Dot(currentOrientation, desiredOrientation));
    Real32 const kDampingFactor = Cmath::Limit(angle / kDampingAngle, 1.0f); // linearly proportional
    Real32 angularLimit = angularSpeed * deltaTime * kDampingFactor;
    CQuaternion newOrientation = CQuaternion::LookAt(currentOrientation, desiredOrientation, angularLimit);
    -

    始终记住我们有两个矛盾的要求:平滑移动和保持角色合理视角

    -

    Springs and PID controllers

    -

    In practice, tuning the characteristics of the controller to achieve -this behavior can be time-consuming but very worthwhile

    -

    Free-Look

    -

    First person free-look

    -

    很多一人称游戏采用circle -strafing技术允许玩家free-look,有些游戏也只允许垂直方向的朝向变化,当然也要限制最大角度

    -

    Third person free-look

    -

    用一个圆锥体围绕look-at点,并使用一个弹簧在没有输入指令的时候令相机能够回到初始位置

    -

    Free-look orientation -determination

    -

    决定free-look朝向通常有两种方法:self-centering和non-centering

    +

    Dynamically +Generated Movies/Replay Cameras

    +

    为了实现回放,可以记录初始游戏状态和玩家操作,然后模拟行为。一些回放系统还支持观测者视角。

    +

    有三种形式的回放相机:reproduction, scripted, dynamically +generated:

    +
      +
    • Reproduction cameras/keyframed cameras: +在固定的时间间隔记录位置和朝向信息,实现简单但可能依赖于其他非keyframed游戏事件
    • +
    • Scripted cameras: +是可交互与预定义的结合。Replay相机的位置可以采用下述的形式: +
        +
      • Discrete camera behaviors: 此类相机perform jump +cuts或者预定义相机位置之间的插值
      • +
      • Path-based motion: 使用设计师预定义的相机路径
      • +
      • Slaved motion: 根据特定物体设置相机位置 +类似地,相机的朝向(甚至FOV)可以采用如下的形式:
      • +
      • Fixed orientations: 固定朝向
      • +
      • Pre-defined orientations: 预定义朝向
      • +
      • Object tracking: 跟踪游戏物体调整朝向
      • +
      • Object framing: +保证游戏物体保持与显示设备一致的相对距离,此时物体的显示大小可以通过改变FOV调整
      • +
    • +
    • Dynamically generated cameras: +在回放的时候动态生成相机,如死亡视角、高亮操作等等
        -
      • Self-centering free-look: -玩家输入会让相机朝向改变,通常把遥感值映射为相机朝向的速度
      • -
      • Non-centering free-look: 在没有玩家输入时相机会保持现在的朝向
      • +
      • 死亡视角相机:可以通过一个围绕角色的stationary相机实现,或者使用一些预定义的相机位置/朝向
      • +
      • 重生相机:当玩家进入/重进游戏时播放的相机
      • +
    -

    Common orientation problems

    -

    一些常见的相机朝向的问题:

    +

    一些常用的相机特效:

      -
    • Gimbal lock: 当相机forward与世界up -axis平行时发生,要么不让相机平行于up axis,要么用四元数构造变换矩阵
    • -
    • Vertical twist: 当使用三人称相机时,如果相机朝向接近平行于up -axis则会出现这个问题,表现为相机快速围绕up -axis旋转,常发生于相机在墙角的情况,此时可以限制相机围绕up -axis的旋转速度,或者禁止相机朝向改变直到相机离玩家足够远
    • -
    • Roll: 飞行模拟游戏会经常使用roll
    • -
    • Orientation noise: 使用high-pass filter移除不想要的朝向变化
    • -
    • Rapid orientation changes: -当允许玩家快速移动/改变朝向,或玩家在相机下方或上方移动时,相机的朝向就会快速改变,带给玩家糟糕的体验
    • -
    • Frustum culling of the player character: -三人称游戏必须首先考虑frame玩家,切通常相机直接看向玩家前进的方向,但也要让玩家保持在view -frustum中
    • +
    • Reorientation changes: +引入朝向误差,模拟人类持相机的行为,但要注意摇晃的幅度
    • +
    • Camera shake: +相机抖动通常在渲染过程中实现而非游戏世界中,因为这不会改变相机的实际位置,注意在第三人称游戏中应当控制相机抖动的幅度和频率
    • +
    • Roll: 用于营造失控的感觉,比如用在赛车游戏中
    • +
    • FOV/DOF: 注意DOF不要频繁使用
    -

    Chapter 8: Navigation and -Occulusion

    -

    Dynamic determination of how the camera should reach its desired -position is referred to here as navigation

    -

    The Cemera as an AI Game -Object

    -

    需要假定环境是封闭的,即有碰撞表面

    - -

    相机的寻路需要考虑环境限制

    -

    Dynamic navigation -techniques

    -

    主要包括以下方法:

    +

    Scripting

    +

    Scripting通常指设计师手动指定相机的位置和朝向,要么在编辑器中进行编辑,要么使用Maya等工具编辑后直接导入

    +

    一个典型的scripting system应当包括:

      -
    • Ray casting: -用射线决定是否存在遮挡物体,但是可能会有效率问题,此外,可以增加射线覆盖更大区域。另一种方法是使用ray-casting -hysteresis,即在多个update中累积ray -cast信息,并生成一个可能碰撞的概率图,从而减少每次更新的ray -cast数量。Ray -cast可以用于帮助相机移动。要对物体进行分类以区别哪些需要ray casting -
      For all ray casts
      if ray cast successful
      No influence applied
      if ray cast fails
      Scale influence factor by distance of ray cast collision from target
      Add influence to desired camera position
      End
    • -
    • Volume projection: -该方法把一个volume从相机位置投影到目标位置,用来确定相机的移动是否合法
    • -
    • Sphere/cylinder collisions: -碰撞体的半径应该比近平面距离略大,理论上,碰撞体应该完全包含近平面frustum的四个顶点。在第一人称游戏中,可以不用给相机加碰撞体,因为一般有角色碰撞体即可,但是也要保证近平面被角色碰撞体包围
    • -
    • Dynamic path finding: High-level -solution负责volume之间的整体移动,low-level -solution负责避免碰撞。相机AI不同之处在于移动往往很短,因此更要关注low-level的需求
    • -
    • Dynamic path generation: 不同于path finding,path -generation使用的是游戏中动态生成的信息,所以生成的path可能会动态改变
    • -
    • Visibility and rendering solutions: -这种技术首先决定相机据目标物体的位置,基于一些其他限制,得到可能的相机位置点,对于每个可能的位置,令相机朝向角色,就可以看当前视角是否存在遮挡。决定好位置吼,就可以计算一条移动路径,但是也要注意路径中的遮挡和碰撞
    • -
    • Colliders: -在相机周围添加collider,每个collider都会发射射线,如果有遮挡出现,则移动相机位置 -
      stl::vector<Vec3> influence; 
      // might be useful as a class
      for (int i = 0; i < colliders.size(); ++i)
      {
      influence.push_back(colliders[i].offset * colliders[i].GetWeighting());
      // the weighting depends on line of sight and/or
      // other factors such as the relative collider position
      }
      // will need to get an average or similar
      return GetCentriod(influence);

    • +
    • 调整相机位置和朝向
    • +
    • 转场
    • +
    • 相机插值
    • +
    • FOV变化
    -

    Pre-defined navigation -techniques

    -

    一些常见的pre-defined navigation技术:

    +

    Scripting system最好还支持编辑时预览,尽管非常困难

    +

    Editing

    +

    编辑就是把所有的shot重新组织、拼接的过程,主要包含以下内容:

      -
    • Pre-defined paths: 预先做好路径,根据玩家操作在路径上移动和朝向
    • -
    • Path motion behaviors: 有几种方法去限制相机的移动 +
    • Shot selection: 镜头选择
    • +
    • Framing/composition: +电影业通常对镜头的摆放有非常具体的标准以达到特定的情感效果,即使镜头离物体的距离固定,物体在屏幕中的位置也会极大影响呈现的效果,通常遵循三分法则(rule +of thirds)
    • +
    • Transitions: 有几种常见的转场方式:
        -
      • Constrained path camera: 相机被固定在path上以避免碰撞
      • -
      • Follow path camera: 相机固定在path上,但移动方式可以有多种方式
      • +
      • Jump cut: 遵循30度法则
      • +
      • Cross fade or dissolve: 此类转场可以降低渲染性能要求
      • +
      • Iris transition: 以屏幕一点为圆心向四周展开
      • +
      • Wipe(s): 擦拭转场
      • +
      • Viewport transitions: 视口转场
    • -
    • Pre-defined volumes
    • -
    • Attractors/repulsors: -相机会被其他pre-defined的位置、区域、物体所吸引/排斥,吸引力/排斥力的大小取决于相机离该物体的距离和朝向。在合适的时候用attractors/repulsors可以帮助相机避免穿模和在狭窄地区顺利通过,而不需要hand-scripted
    • -
    • Flow fields: Flow -fields是向量的稀疏数列,通常稀疏地分布在2D平面或者3D -Volume内。当相机穿过flow -field时,离相机近的向量会对相机朝向的方向施加一个力。Flow -field常用于一个额外的因子而不是单个因素
    • -
    • Influence maps: 类似flow field,但是通过目标对象离influence -map的相对距离决定而非camera本身,influence -map中的每个点都是相机的可选位置
    • -
    • Potential fields: 基于electrostatic -forces,即对一个物体施加的力的效果取决于发力点和目标物体之间的距离,类似attractors/repulsors
    -

    Occulision

    -

    Occulusion determination

    +
    +Rule of thirds示意。 + +
    +

    Tools

    +

    如果电影镜头使用游戏引擎进行渲染,即用常规游戏环境制作镜头,则最好让编辑器支持直接控制cinematic +sequences,包括:

      -
    • Ray casting: 通常使用三个部分进行遮挡检测——头部、躯体和脚部
    • -
    • Volume casting
    • -
    • Rendering techniques: +
    • 直接对相机位置和朝向的控制
    • +
    • 通过另一个物体控制相机
    • +
    • 控制相机之间变化
    • +
    • 通过相机触发游戏事件
    • +
    • 镜头的转场
    • +
    • 控制FOV
    • +
    • 即时预览
    • +
    • 额外的编辑和debug工具
    • +
    +

    上述属性都可以通过一条曲线完成(类似Unity),横坐标是elapsed +time,纵坐标是具体值

    +

    Part 2: Design Principles

    +

    Chapter 4: Camera Design

    +

    Interactive 2D Camera +Systems

    +

    2D游戏通常使用tile +map,每个tile都是一个小图片,且使用hash存储,因此极大节省内存

    +

    2D相机的主要功能之一是让角色一直位于屏幕可视范围,比如可以使用scrolling,即让背景做相对运动。下面是一些常见的scrolling类型:

      -
    • Flat-shading using identifiers as color values: -待进行遮挡检测的物体使用flat -shading和一个标识颜色,环境物体使用不同的颜色渲染,然后可以通过display -buffer检测物体的遮挡部分
    • -
    • Z-buffer analysis: 直接用depth buffer进行检测
    • -
    • Rendering hardware occulusion queries
    • -
    +
  • Continuous: 背景的scrolling随着游戏连续进行
  • +
  • Character-relative: 相机的运动与角色的运动同步
  • +
  • Directional lag: 期望的相机位置基于角色运动的方向
  • +
  • Burst: 期望的相机位置只有当角色到达某个位置或距离时才变化
  • +
  • Screen-relative: 只有在角色超出预定义的边界时相机才开始移动
  • +
  • Region-based: 当角色超过相对于世界的边界时相机才开始移动
  • -

    Occulusion prediction -methodologies

    -

    可以用一个predictive相机预测遮挡

    -

    Line of Sight

    -

    Resolving line of sight -problems

    -

    可能有时候相机不能快速移动保持对目标物体的LOS,下面是一些可能的解决方案:

    +

    有时候相机的位置要独立于角色位置。对共用一个viewport的多人游戏来说,所有角色的位置共同决定了相机的位置。解决方案:

      -
    • Prevention: 手动调整相机位置让它避免出现loss of LOS
    • -
    • Graphical changes: -可以把遮挡物体透明掉或去掉,也可以着重突出角色(如描边、改变颜色、增加indicator)
    • -
    • Finding a desirable position: -可以以角色为中心旋转,或者使用visibility -octree信息,一旦一个位置被发现,则直接把相机relocate到该位置
    • -
    • Teleportation/jump cut: -可以把相机沿着LOS的方向移动,直到没有遮挡(注意要考虑相机的近平面位置与collision -volume)
    • -
    • Retaining control orientation: 相机移动后注意保持相同的retaining -control orientation
    • +
    • 允许相机zoom out
    • +
    • 限制玩家移动
    • +
    • 分屏
    • +
    • 允许玩家在屏幕外移动
    • +
    • 传送玩家
    -

    Path generation

    -

    该方法生成一条路径,相机沿着路径移动直到遮挡消失,生成的路径不仅要避免碰撞,并且也要符合美学要求:

    +

    Cinematic 2D Camera Systems

    +

    2D cinamatic camera system的一些典型特征包括:

      -
    • Follow character movement: -记录玩家移动的路径并让相机沿着路径移动
    • -
    • Ledge avoidance: -玩家从悬崖边缘下落时相机可能因为无法穿透物体而出现问题,解决方案可能有: +
    • Viewport panning control: 控制运动路径
    • +
    • Zoom in/out: 放大缩小
    • +
    • Object tracking: 目标跟踪
    • +
    • Keep an object within screen bounds or other arbitrary +viewport-based constraint: 带有限制的移动
    • +
    +

    Interactive 3D Camera +Systems

    +

    交互式3D相机系统的难点在于提供一个上下文合适的视角,包括艺术性与游戏性,特征包括:

      -
    • Pre-defined scripting solutions for all ledge situations
    • -
    • Use pre-defined solutions for difficult to resolve cases, especially -in confined spaces: 可以用stationary cameras或者spline paths
    • -
    • Teleport the camera to a new position if LOS fails due to the ground -being between the camera and its target: 这可能需要检测剪玩家的高度
    • -
    • Use player movement hysteresis to provide an approximate path taken -by the player character or target object
    • -
    • Interrogate the surrounding geometry to dynamically choose a path -based on the last position at which the player was visible to the -camera: 该位置给出了关于ledge的位置信息用于camera -path,但要保证相机不能离物体太近,也不能让相机垂直朝向
    • -
    -
  • Vertical column avoidance: Predictive cameras一般能解决该问题
  • -
  • Pre-computed solutions
  • +
  • 渲染投影类型(透视、正交)
  • +
  • 决定相机期望位置(navigation)
  • +
  • 相机移动的方式
  • +
  • 决定相机期望朝向
  • +
  • 相机旋转的方式
  • +
  • 相机其他属性的动态改变(FOV, etc)
  • +
  • 相机插值
  • +
  • Fail-safe handling
  • +
  • 多视口/分屏
  • -

    Avoiding loss of LOS

    -

    如果不能避免loss of LOS,我们可以采取一些方法进行弥补:

    +

    Cinematic 3D Camera Systems

    +

    当设计3D cinematic camera system的时候有几个有用的建议:

      -
    • Instant movement: 有时候很实用
    • -
    • Fade out the obscuring geometry or objects: 会降低玩家沉浸感
    • -
    • Do not render obscuring gheometry or objects at all: -可以用一个timer,只有时间到了才取消渲染
    • -
    • Render a graphical representation regardless of occlusion
    • -
    • Visibility pre-computation
    • +
    • Duplicate controls and functionality from modeling packages: +复用现有建模包中的功能,让艺术家能够用得舒服、更有效率
    • +
    • Develop a common terminology: 尽可能用标准的专业术语
    • +
    • Consider pre-rendering cinematic sequences: +用游戏引擎渲染sequence
    • +
    • Distinguish non-interactive sequences from regular game play: +要让玩家意识到在播放cinematic +sequences时不能再操纵角色了,比如调整aspect ratio
    • +
    • Allow an early out after viewing scene once: +让观众有机会跳过cinematic sequence
    -

    Fail-safes

    -

    Fail-safe检测可以分为三类:

    +

    2.5D Camera Systems

    +

    2.5D相机系统有自身的优势,比如让游戏玩家更容易理解和接受游戏

    +

    Display Devices

    +

    让游戏分辨率适应显示分辨率的方法:

    +
      +
    • Resize: 通过render buffer和viewport匹配显示设备的分辨率
    • +
    • Pan and scan: 裁剪
    • +
    • Stretch: 伸缩
    • +
    +

    Camera Design process

    +

    首先,游戏的整体体验/玩法需要制定下来,然后再制定相机的宏观设计框架。相机设计的总体流程是:

    +
      +
    • Examine high-level design goals: +明确在游戏的不同部分相机有何相同之处,是否需要单个representation +style,玩家有怎样的体验
    • +
    • Evaluate player character abilities: +玩家怎么移动、怎么与环境交互
    • +
    • Determine scope of environments: +游戏场景是怎样的,要提前根据场景做camera prototyping
    • +
    • Define base camera behaviors: 尽快确定相机的基础功能
    • +
    • Determine area-specific requirements: 明确特殊区域
    • +
    • Technical evaluation of cameras: +关注处理器开销、内存开销、脚本能力、Debug能力
    • +
    +

    在相机设计的时候可以问一些问题,这有助于我们更好地设计:

      -
    • Per-update: 在一些情况下必须在每个update进行fail-safe检测 +
    • Player abilities and controls
        -
      • Geometry interpenetration of the camera near plane: -可以用一个足够大的collision volume避免
      • -
      • An invalid (non-normalized) camera transformation matrix: -此时可以orthonormalize或者reconstruct矩阵,或者可以回到上次正确的旋转矩阵
      • -
      • The camera is external to the game world
      • -
      • Excessive rotation required aounrd the world up-axis in one update: -限制旋转角度
      • +
      • 角色怎么移动的?加速度是什么?是否能跳/飞?
      • +
      • 角色的移动能力是否最终确定了?
      • +
      • 相机与玩家的关系是怎样的?
      • +
      • 是否应该保持与玩家朝向相对固定的朝向?
      • +
      • 玩家想要看到什么?
      • +
      • 相机应该关注角色还是角色前的某个点?
      • +
      • 是否有必须可视的部分?
      • +
      • 除了角色之外是否还有其他物体必须可视?
      • +
      • 相机的朝向是否需要改变?
      • +
      • 玩家角色怎样被控制?
    • -
    • Frequent: 有时候每隔几次update进行一次fail-safe检测 +
    • Environmental concerns: 场景设计应该围绕玩法和相机限制
        -
      • LOS to garte object extremities
      • -
      • Distance from desired position
      • +
      • 角色在怎样的场景中移动?开放还是封闭,宽敞还是狭窄?
      • +
      • 场景是否影响相机的位置和朝向?
      • +
      • 相机是否应该被放在场景外?如是,如果在常规玩法中切换?
      • +
      • 相机是否会在动态变化的场景中跟随玩家?受到阻挡怎么办?
      • +
      • 相机是否应当避免复杂物体?
      • +
      • 相机是否应当限制在具体的路径或表面?
      • +
      • 哪种presentation style更好?
      • +
      • 在场景中相机插值是否还生效?
      • +
      • 是否有必要给相机加上特定的碰撞体?
    • -
    • Infrequent: 每隔几秒检测一次 +
    • Technical concerns
        -
      • Significant occlusion of player character (3rd person view -only)
      • -
      • Camera distance from target is too far: 会让目标看起来太小
      • +
      • 是否有技术限制?包括渲染性能问题等
      • +
      • 如果相机会穿透物体,是否支持透明化?
      • +
      • 是否需要fail-safes以处理未预料的意外情况,比如关门?
      • +
      • 是否允许操纵相机?
      • +
      • 场景是否可能遮挡玩家而无法解决?此时场景是否需要进行改变?
      • +
      • 是否支持相机插值?如何避免插值间的遮挡?
    -

    一旦触发fail-safe condition,最好是立刻将相机移动到新的safe -position,通常是jump -cut。另一个方法是把相机移动回过去的已知点直到满足条件

    -

    Chapter 9: Motion and -Collision

    -

    Camera Movement Sequence

    -

    我们可以按照下述过程移动相机:

    +

    对于相机的要求、限制应该在项目早期就明确,除非是个别例外,并且要避免在项目开展后大改相机系统。因此,要在项目商讨环节把需求都明确清楚

    +

    团队中的camera +designer不仅需要负责相机表现,而且也需要负责大部分情况下的相机解决方案。Lead +camera +designer需要具备美学sense,包括游戏内容的呈现和玩家操作、感知的表现,此外,他还需要推动团队提高认知

    +

    Camera Design Guidelines

    +

    一些相机设计的建议:

      -
    • Determine the desired position
    • -
    • Constrain the desired position accordingly
    • -
    • Generate a prospective movement toward the desired position
    • -
    • Test to see if the prosepctive movement will cause collision -(optional)
    • -
    • Resolve the collision (optional)
    • -
    • Generate and validate an alternative movement (optional if first -move fails)
    • -
    • Move the camera to the new position
    • +
    • Attempt to keep the player character in view (3rd person +cameras)
    • +
    • Prevent the camera passing through (or close to) game objects or +physical environmental features
    • +
    • Do not require the player to manipulate the camera simply to play +the game -- unless it is a design requirement
    • +
    • Allow camera manupulation when possible or dictated by game design +requirements
    • +
    • Minimize unintentional camera motion whenever possible
    • +
    • Ensure camera motion is smooth
    • +
    • Limit the reorientation speed of the camera
    • +
    • Limited roll should be allowed in most regular game cameras
    • +
    • Do not allow the camera to pass outside the game world
    • +
    • Retain the camera position with respect to the player when instantly +moving th camera to a new position (3rd person cameras)
    • +
    • Do not focus directly on the player character when it is moving
    • +
    • Retain control reference frame after rapid or instantaneous camera +motion
    • +
    • Avoid enclosed spaces with complex geometry (3rd person +cameras)
    -

    Character motion

    -

    首先看看人物移动会怎样影响第一人称和第三人称相机

    +

    Chapter 5: Camera Solutions

    +

    Game Genre Camera Solutions

      -
    • First person cameras: -注意相机位置通常不是眼睛位置,因此要注意fov和aspect -ratio。有时候玩家跨越世界物体时可能造成traversal -jitter,因此可以增加vertical damping
      // A typical damping scheme for aviuding unwanted noise in the verticasl 
      // motion of the camera is to simply limit the amount allowed per update.
      float const kMaxZMotionPerSecond (0.25f);
      // desired maximum motion
      float zMotion = newPosition.GetZ () - oldPosition.GetZ ();
      float const maxZMotion = kMaxZMotionPerSecond * deltaTime;
      zMotion = Math::Limit (zMotion, maxZMotion);
      newPosition.SetZ (oldPosition.GetZ () + zMotion);

      // The smoothing is relatively harsh: the amount of vertical motion applied
      // is constant and thus will possibly have discontinuities.
      // An alternative is to use a critically damped using spring or PID controller.
      float const zMotion = newPosition.GetZ () - oldPosition.GetZ ();
      verticalSpring.SetLength (AbsF(zMotion));
      // update the spring length
      // Here the target spring length is zero, and need not
      // be set each time. Typically the spring length is unsigned,
      // so that must be dealt with.
      float const newZMotion = verticalSpring.Update (deltaTime);
      if (zMotion > 0.0f)
      newPosition.SetZ (newPosition.GetZ() - newZMotion);
      else
      newPosition.SetZ (newPosition.GetZ() + newZMotion);
    • -
    • Third person cameras: 必须解决相机加速小于角色的问题(motion -lag),和相机减速小于角色的问题(overshooting)
    • -
    -

    Movement methods

    -

    一些移动相机的方法包括:

    +
  • FPS游戏:目标是提供沉浸式的体验
      -
    • Instantaneous motion: 直接把相机移动到期望的位置,但要注意: +
    • 位置:一般与玩家视角同步,但并不是玩家眼睛的位置,因为武器、手臂等必要元素通常不能从眼睛位置可视
    • +
    • 朝向:通常与人眼可视方式一样,即free-look,有时候可帮助玩家自动调整垂直方向的朝向
    • +
    • 武器或手臂位置:武器与手臂通常可见
    • +
    • 与外部观测的不同:多人游戏中其他玩家看到的角色可能不同
    • +
    • 与三人称之间的切换:常用jump cut实现切换
    • +
    • Zoom effects
    • +
    • Lack of peripheral vision
    • +
    • Aiming position
    • +
    • Motion sickness
    • +
    • Camera shaking
    • +
    • Cmaera bob: 角色运动时相机的轻微移动
    • +
    • Idle wandering
    • +
    • Scale: 人物在高速移动时往往会scale +尽管相机和人物之间保持相对距离,但是相机也对影响人物移动的因素相当敏感,比如在复杂的地形上,角色可能被垂直提高一段距离,这通常不到一秒钟的时间,但是仍然会对相机产生影响,导致glitch现象,这可以通过增加垂直方向的damping解决,这只有在角色穿越崎岖地表时才生效。注意一人称的damping值要比三人称小
    • +
  • +
  • Character/action +adventure游戏:可以根据角色的技能决定相机的行为,比如大致分为两类:地面与飞行
  • +
  • Stealth: 相机会随着使用物品或武器而变化
  • +
  • 3D platform: 主要分为两种形式:free form与pre-determined
      -
    • 可能也要改变朝向
    • -
    • 可能需要检测新的位置是否离环境物体太近、没有在物体内
    • -
    • 尽可能保持control reference frame
    • -
    • 尽可能把所有该类型的变换定义为一个函数
    • -
    • 如果出现多普勒效应,则应限制相机移动的最大速度
    • +
    • free-form: 相机的位置与距离可以由玩家操控
    • +
    • pre-determined: 严重依赖于特定的Game +play,同时也要兼顾相机的效果
  • -
  • Locked
  • -
  • Proportional controller: 该方法在目标也移动的时候表现不好 -
    Vec3 const deltaPosition = desiredPosition - currentPosition;
    // the distance at which velocity damping is applied
    float const kDampDistance (5.0f);
    float const K = Math::Limit (deltaPosition.Magnitude() / kDampDistance, 1.0f);
    // Limit constant to 0..1 based on distance
    Vec3 const cameraVelocity = deltaPosition.AsNormalized () * K;
    Vec3 const newPosition = currentPosition + cameraVelocity * deltaTime;

    // We can help solve the problem of the camera being left behind by adding a
    // portion of the target object's velocity into the original equation.
    Vec3 targetVelocity = (desiredPosition - previousDesiredPosition) / deltaTime;
    Vec3 = cameraVelocity = (deltaPosition.AsNormalized () * K * deltaTime) + (targetVeclocity * T);

    // To have smooth motion we need to accelerate the camera over time
    // with a limiter to provide some degree of lag in the motion.
    float const acceleration = Math::Limit ( (desiredVelocity - currentVelocity), kMaxAcceleration);
    Vec3 const currentVelocity += acceleration * deltaTime;
    Vec3 const desiredPosition = currentPosition + (currentVelocity * deltaTime);
  • -
  • Physical simulations
  • -
  • Springs: -当目标物体朝相机移动或突然停止时,相机可能出现overshooting问题,我们希望critical -damping,即无论目标物体怎么动,都不会出现overshooting
  • -
  • PID controllers: proportional integral derivative (PID) -controllers,把feedback应用到控制器中减少当前值和期望值的错误量 -
    // The usage of PID controller
    Vec3 const currentDelta = currentPosition - desiredPosition;
    float const currentDistance = currentDelta.Magnitude ();
    float const newDistance = PIDController.Update (desiredDistance, currentDistance, deltaTime);
    Vec3 const currentPosition = desiredPosition + (currentDelta.AsNormalized () * newDistance);
  • -
  • Circular movement: 相机的运动由reference -point到当前位置和期望位置之间的夹角决定,因此需要计算其角速度,有两个方法计算: +
  • RPG: 常用三人称相机
  • +
  • Scrolling
  • +
  • Sports: TV-style presentation may be very desired
      -
    • Constant angular velocity: 不管半径多大,以固定的角速度旋转
    • -
    • Constant linear velocity: 保持固定线性速度
      // A typical implementation of moving a camera by constant angular velocity might be:
      Vec3 cameraBearing = Vec3 (currentPosition - referencePoint);
      Vec3 desiredBearing = Vec3 (desiredPosition - referencePoint);
      float const radius = cameraBearing.Magnitude ();
      cameraBearing.Normalize ();
      desiredBearing.Normalize ();
      float const angularMotion = angularVelocity * deltaTime;
      // rads/update
      Quat const q = Quat::ShortestRotationArcClamped (cameraBearing, desiredBearing, angularMotion);
      Vec3 const newBearing = q * cameraBearing;
      // rotate the original bearing
      Vec3 const newPosition = referencePoint + (newBearing * radius);

      // An implementation of constant linear velocity might be:
      Vec3 cameraBearing = Vec3 (currentPosition - referencePoint);
      Vec3 desiredBearing = Vec3 (desiredPosition - referencePoint);
      float const radius = cameraBearing.Magnitude ();
      cameraBearing.Normalize ();
      desiredBearing.Normalize ();
      float const arcLength = linearVelocity * deltaTime;
      float const angularMotion = acosf (arcLength / radius);
      // units/update
      Quat const q = Quat::ShortestRotationArcClamped (cameraBearing, desiredBearing, angularMotion);
      Vec3 const newBearing = q * cameraBearing;
      // rotate the original bearing
      Vec3 const newPosition = referencePoint + (newBearing * radius);
    • +
    • Single-participants sports: 相机只需要focus on玩家或者单个物体
    • +
    • Two or four-participant sports: 联机or分屏
    • +
    • Team sports: 预留一个control +command标识当前正在控制哪个角色以及哪些角色可以被控制
    • +
    • Court and indoor sports: +对球类运动而言,难点在于追踪及可视球;对桌类运动而言,要提供整个桌面的视野并简化玩家操作
    • +
    • Outdoor sports: +对于场地运动,可以提供能够自主操作的相机;自由运动;轨道运动
    • +
    • Abstract sports
  • -
  • Interpolation: -考虑在两个相机的速度之间插值,为了避免discontinuity,要让加速度匹配而不仅仅是速度
  • - -

    Smoothing and damping -techniques

    -

    Damping方法通常要用ease -functions,把一个输入值(0到1之间)映射到相同区间,只不过是非线性映射且开始和结尾的导数为零

    +
  • Racing games: +可提供额外的视角以观测其他人的位置,增大FOV可以制造物体快速通过玩家的感觉
  • +
  • Ground vehicles
  • +
  • RTS
  • +
  • Flight simulation: 一个重要的方面是是否允许相机随着飞机一起roll
      -
    • Motion damping: 可以通过添加vertical damping减少颠簸感
    • -
    • Motion filters: 用一个低通滤波过滤噪音
      Vec3 const movementDelta = newPosition - currentPosition;
      if (movementDelta.Magnitude () > kMovementThreshold) {
      currentPosition = newPosition;
      } else {
      // ignoring small changes may cause the camera never to move at all!
      }
      -下面是一种改进的方法(使用finite impulese response and infinite impulse -response filters):
      Vec3 const movementDelta = newPosition - currentPosition;
      if (!close_enough (movementDelta, Vec3::Zero ())) {
      float distance = movementDelta.Magnitude ();
      distance = mMovementFilter.Update (distance);
      currentPosition += movementDelta.AsNormazlied () * distance;
      }
    • +
    • Realistic flight models: 此类游戏通常从驾驶舱或飞机后侧观察
    • +
    • Non-realistic flight models
    • +
    • Artificial horizon: 可以用仪器监控飞机的pitch或banking
    • +
    • Sensation of motion: +可以用云层或航迹云增强飞机的速度感;航天器可以用小的残骸增强速度感;也可以通过音效、FOV等方式
    • +
  • +
  • Adventure: present a very cinematic experience to the player
  • +
  • Puzzle/party games/board games
  • +
  • Fighting/close combat: +场景一般很简单以便于相机摆放,需要一些相机移动突出角色的操作
  • -

    Motion constraints

    -

    一般有下述几种相机约束:

    +

    Multi-Player Camera +Solutions

    +

    多人共享屏幕时,相机设计会有很大难度,有时候可以通过alternate +players解决,但多数情况下并不适用。一般来说可以有两种解决方法:

      -
    • Vertical motion constraint: -用于避免不想要的图形效果如穿模,比如避免水面穿帮可以用下面的代码: -

      float const kMinimumZDistance (0.1f);
      float const zDistance (currentPosition.GetZ () - waterPlane.GetZ ());
      if (fabs (zDistance) < kMinimumZDistance)
      {
      currentPosition.SetZ (waterPlane.GetZ () + Math::Sign (zDistance) * kMinimumZDistance);
      }
      -显然,上述代码的问题在于相机在进入水面后会出现不连续移动,但这无法避免,但可以用特效过渡

    • -
    • Render geometry proximity: 用collision volume解决

    • -
    • Distance constraints: -可以让相机和玩家保持固定的距离或最小/最大距离,但是可能会有问题 -

      Vec3 const unconstrainedOffset = currentPosition - desiredPosition;
      float const currentDistance = inverseDirection.Magnitude ();
      float const newDistance = Math::Clamp (kMinDistance, currentDistance, kMaxDistance);
      Vec3 const newOffset = unconstrainedOffset.AsNormalized () * newDistance;
      currentPosition = desiredPosition + newOffset;

    • -
    • Planaer constraints

      CPlane const plane (normal, constant);
      Vec3 cameraPosition (GetCurrentPosition ());
      float const planeDistance = Vec3::Dot (plane.GetNormal (), cameraPosition) - plane.GetConstant ();
      if (planeDistance < kMinDistance)
      {
      cameraPosition += plane.GetNormal () * (planeDistance - kMinDistance);
      }

    • -
    • Surface constraints

    • -
    • Volume constraints

    • +
    • 分屏
    • +
    • 单屏包含所有玩家
    -

    Player camera control

    -

    一个原则: > The player should be not REQUIRED to manipulate the -camera simply to play the game, unless explicitly dictated by the game -design.

    -

    If camera-relative, the sheer act of moving the camera changes the -control reference frame and "pulls the rug out from under the player's -feet". Sadly, some games seem to treat this behavior as acceptable. It -is not. Changing the control reference frame without player knowledge is -counter to good game play practice.

    +

    下面是对相关技术的介绍:

      -
    • Maunipulation of the camera position: -有时候允许玩家直接操纵相机,分为2D和3D相机 +
    • Single-screen techniques: 格斗游戏、合作游戏中常见
        -
      • 2D相机: 2D游戏通常会限制相机的移动以防看到游戏外的场景
      • -
      • 3D相机: 在不同相机之间切换可以使用插值或jump cut
      • +
      • 相机的位置很难确定
      • +
      • 通常相机位置固定在一个离游戏世界表面相对的高度,而与玩家移动无关
      • +
      • 相机位置可能导致朝向的迅速变化
    • -
    • Observer cameras: 提供观察者相机,即可以随意自由移动的相机
    • -
    • Camera motion validation: 一些游戏不允许操纵相机位置
    • -
    • Positional control constraints: -Character-relative相机通常保持在玩家身后的一定区域内,一般不会让相机面朝角色。相机移动要尽可能平滑和慢以避免player -disorientation
    • -
    -

    Camera position control -schemes

    -

    主要有两种方法决定相机位置:

    -
      -
    • Character-relative: -类似unity的free-look相机,但需要加一些限制,比如相机的移动范围是一个cylindrical而不是整个球面、相机在人物朝向的身后一定范围内。一些相机允许半自动的移动,比如移动向一个危险区域时
    • -
    • World-relative: -此时相机的朝向和玩家朝向没关系,比如RTS、运动、3D平台游戏中的相机
    • -
    -

    Manipulation of camera -orientation

    -
      -
    • First person cameras
    • -
    • Third person cameras:
    • -
    -

    Automated camera -positioning and orientation

    -

    除了cinematic -sequences之外,也有一些情况会需要相机自动改变位置和朝向:

    +
  • Split-screen techniques
      -
    • In response to player controller usage: 玩家自由选择视角
    • -
    • In response to game play requirements: -在三人称游戏中,有时候我们想保证某个物体一直在视野内,有时候,会通过相机给玩家辅助(比如在跳跃时增加pitch)
    • -
    -

    Debug camera control

    -

    该相机需要在不影响游戏的情况完全由用户操作,同时,如果能在降低游戏运行速度的情况能以正常速度操作debugging -camera,则会非常有利于debug

    -

    Camera Collisions

    -

    The importance of camera -collisions

    -

    Camera collision可能引起的问题:

    +
  • 每个玩家的分屏区域一样大
  • +
  • 根据活跃玩家动态分屏
  • +
  • 为了提升性能,尽量让所有玩家都处于同一个环境中
  • +
  • 尽量减少玩家获取的信息
  • +
  • 避免把重要的物体放在设备屏幕边缘
  • + +
  • Transitioning from full-screen to split-screen
      -
    • The collision prevents the camera from moving to its desired -position
    • -
    • Discontinuous motion may be caused by collisions
    • -
    • Occulusion of the target object
    • -
    • Camera near plane intersects with geometry
    • -
    -

    Collision determination

    +
  • 保持相机相对距离相同
  • +
  • UI元素要重新排布
  • +
  • FOV要根据aspect ratio重新调整
  • + +
  • Transitioning from split-screen to full-screen
      -
    • Object collisions
    • -
    • Environmental collisions: 有两种数据——render mesh和collision -mesh
    • -
    • Collision primitives: 通常来说,相机会被表示为球体用于碰撞检测
    • +
    • 玩家移动地足够近的时候
    • +
    • UI元素要等transition完成时再移动
    • +
    • 相机相对位置保持不变
    • +
    • FOV调整
    • +
  • -

    Collision geometry design

    -

    下面是一些设计场景的tips:

    +

    Gamera Scripting

    +

    What is Meant by Scripting

    +

    定义与控制物体加偶和的过程被称为scripting,在游戏中,指控制游戏事件发生的事件与物体之间的交互。Scripting允许设计师在没有程序员介入的情况下完成迭代

    +

    Types of Scripting

    +

    我们希望构建一个与游戏类型无关的scripting系统,主要由两个方面组成:scripting +language和event messaging

    +

    Scripting languages

      -
    • Avoid tightly confined spaces
    • -
    • Allow sufficient space for the camera
    • -
    • Steep steps
    • -
    • Ledges/overhangs
    • -
    • Low ceilings
    • -
    • Doorways: -第一,玩家可能移动太快以至于门在玩家和相机之间关闭,此时可以让门保持开启直到相机穿过;第二,玩家可能沿着门缝移动并导致相机无法跟随玩家,此时可以让相机沿着门缝的轴移动;第三,玩家可能站在门缝里,此时要避免相机因玩家旋转而与物体相交
    • -
    • Use the simplest collision mesh possible
    • -
    • Separate collision mesh for the camera
    • -
    • Chamfer all corners: 为此可以让相机沿着collision表面滑动
    • +
    • Text-based scripting languages: +包括pre-compiled和interpreted两类
    • +
    • General-purpose programming languages: 编程语言
    • +
    • Custom scripting languages
    • +
    • Finite state machines
    • +
    • Visual scripting languages
    -

    Collision resolution

    -

    当我们检测到发生碰撞时,就需要决定相机怎么办,可以有以下几种策略:

    +

    Event messaging

    +

    Event +messaging要求定义不同物体之间的关系。典型的能够发送消息的事件包括:

      -
    • Do not move: 此时如果人物移动就可能会有问题
    • -
    • Partial move: 尝试向期望方向移动,如果还是有碰撞则不动
    • -
    • Alternative movement: 改变相机的移动方向
    • -
    • Jump cut: 重新计算desired position并立即切换过去
    • +
    • 关卡开始
    • +
    • 物体加载
    • +
    • 游戏暂停
    • +
    • 进入或退出volume
    • +
    • 玩家受到伤害
    • +
    • 物体到达路径终点
    • +
    • 玩家状态改变
    -

    Disabling collision -detection

    -

    有时候我们不需要相机的碰撞检测。比如第一人称和第三人称视角的切换、path-based -cameras。总之取决于期望的相机效果

    -

    Avoiding camera collisions

    -

    可以动态生成一条相机跟随的路径,或者统一采用半透明的方式。在三人称游戏中,当玩家在墙角或无法移动的地区时,相机可能和角色碰撞,此时可以切换到第一人称中,或者旋转相机调整相机位置

    +

    能够被事件发送的典型消息包括:

      -
    • Future position of player
    • -
    • Predicting potential collisions
    • -
    • Collision prediction
    • -
    • Object repulsion
    • -
    • Movement toward the camera
    • +
    • Start
    • +
    • Stop
    • +
    • Activate object
    • +
    • Delete object
    • +
    • Query state
    -

    Chapter 10: Camera -Mathematics

    -

    摄像机的一些数学:

    +

    Script Objects

    +

    用于定义物体的属性和行为

    +

    Scriptable game hints

    +

    Game hints are script objects that provide one possible type of +runtime mechanism by which designers can override player properties, +game controls or camera properties according to events

      -
    • Camera position +
    • Camera hints: 改变相机的属性或移动,可以被重载的相机属性包括:
        -
      • Offset within the local space of an object
      • -
      • Angular offsets relative to game objects
      • -
      • Valid position determination
      • -
      • Spline curves
      • +
      • 相机行为
      • +
      • 位置朝向
      • +
      • 相对距离
      • +
      • 看向点
      • +
      • 移动速度
      • +
      • FOV
    • -
    • Camera orientation +
    • Player hints
        -
      • Representation
      • -
      • Look-at
      • -
      • Shortest rotation arc
      • -
      • Roll removal
      • -
      • Twist reduction
      • -
      • Determining angles between desired orientations
      • +
      • Prevent motion of the player
      • +
      • Relocate player position
      • +
      • Change player movement characteristics
      • +
      • Flags to indicate special kinds of game play
    • -
    • Camera motion +
    • Control hints
        -
      • Proximity
      • -
      • Damping functions
      • -
      • Springs
      • -
      • Interpolation
      • +
      • Disable specific controls or sets of player controls
      • +
      • Specify the control reference time and the time to interpolate to +that new reference time
    • -
    • Rendering +
    +

    很多时候事件需要有先后顺序,一个sequenced event +manager可以用来定义事件的先后顺序。

    +

    Camera Scripting

    +

    Camera scripting methods

      -
    • Camera space/world space/screen space conversions
    • -
    • FOV conversion
    • -
    • Frustum construction
    • -
    • Perspective projection
    • -
    • Orthographic projection
    • -
    • Isometric projection
    • -
    • Axonometric projection
    • -
    -
  • General camera math +
  • Pre-defined camera scripting
  • +
  • Dynamic camera scripting: 只调整需要的相机属性
  • + +

    DCamera control

      -
    • Digital filters
    • -
    • Spline curves
    • -
    • Interpolation
    • -
    -
  • Camera math problems and fixes +
  • Third person scripting: 用专门的script objects去调整FOV、Focal +Length,filter or rendering effects, fog distance等
  • +
  • First person scripting: 有一些情况也需要对FP相机进行动态控制,比如
      -
    • Floating-point accuracy and precision
    • -
    • Epsilon usage
    • -
    • Base conversion
    • +
    • Traversing up or down ramps or other significantly sloped surfaces: +在上下坡的时候线相机要和坡度一致 根据玩家位置自动pitch。
    • +
    • Jumping: 在高距离跳跃时,能够对准落脚点的方向,但幅度不宜过大 玩家跳跃时自动pitch。
    • +
    • View locking: 锁定目标时相机要朝向目标点 玩家锁定目标。
  • +
  • Non-interactive movie scripting: +最常用的就是位置和朝向的scripting
  • -

    Common Mathematical -Techniques

    -

    Look-at

    -

    此时通过一个旋转矩阵或四元数把当前的相机朝向变换到新的朝向

    -
    Mat4 const Mat4::LookAt (Vec3 const& source, Vec3 const& dest, Vec3 const& worldUpVector)
    {
    Vec3 viewDirection = Vece (dest - source).AsNormalized ();
    float dot = Vec3::Dot (viewDirection, worldUpVector);
    Vec3 unnormalizedUp = worldUpVector - (dot * vewDirection); // 相机的unnormalized Up axis
    Vec3 up = unnormalizedUp.AsNormalized (); // 相机的up axis
    Vec3 right = Vec3::Cross (up, viewDirection);

    // matrix ordering depends on row/column major
    return Mat4 (
    right[X], viewDirection[X], up[X], source[X],
    right[Y], viewDirection[Y], up[Y], source[Y],
    right[Z], viewDirection[Z], up[Z], source[Z]
    )
    }
    -

    注意到上面的变换矩阵是把世界空间中的点变换到相机在(0,0)点的位置

    -

    Roll removal

    -

    为了移除roll,我们需要重新计算view transform的right -axis同时保持当前的forward vector,然后可以通过叉乘决定up vector

    -

    Twist reduction

    -

    在三人称相机中,当物体在相机上方或者下方的时候,可能导致相机快速旋转。一个方法是检测相机绕着forward -vector旋转的速度,但是只有在forward vector几乎在和world up -axis平行的时候

    -

    World space to screen -space conversion

    -

    取决于projection算法

    +

    一些经验法则:

      -
    • Orthographic projection
      uint32 const ScreenX = ObjectX - ViewportX; // assumes top left corner is (0,0)
      uint32 const ScreenY = ViewPortY - ObjectY; // depends on the direction of +Y
      -如果viewport是显示设备的子集,或者viewport涉及缩放,则必须考虑这些因素: -
      uint32 const ScreenX = Viewport.ScreenX + (ObjectX - Viewport.WorldX);
      uint32 const ScreenY = Viewport.ScreenY + (Viewport.WorldY - ObjectY);
    • -
    • Isometric projection
      uint32 const ScreenX = (ObjectX - Viewport.WorldX) * 2;
      uint32 const ScreenY = Viewport.WorldY - ObjectY;
    • -
    • Perspective projection
    • +
    • Apply Occam's Razor: 使用最简单scripting方案,这便于随时修改
    • +
    • Allow for different ways that the camera event may be triggered: +比如有时候只用trigger volume可能不够
    • +
    • Allow previewing of camera motion and orientation: 支持预览
    • +
    • Assess camera scripting requirements early in the design +process
    • +
    • Make the scripting development cycle as efficient as possible: +提高预览和扩展能力
    • +
    • Mimic the behaviors of the tools used by artists where possible: +降低艺术家(设计师)的学习成本
    • +
    • Allow for game play changes: camera scripting system: +游戏玩法改变时能够轻松地修改camera
    -

    Screen space to -camera/world space conversion

    -

    通常来说,神都会被设置为相机的近平面,或者一个特定的距离相机的距离

    -
    Vec3 const ConvertToWorldSpace (Mat4 const& cameraTransform, Vec3 const& screenPosition)
    {
    Vec3 const viewSpace = GetProjectionMatrix ().Inverted ().MultiplyOneOverW (screenSpacePosition);
    Vec3 const worldSpace = cameraTransform * viewSpace;
    return worldSpace;
    }
    -

    FOV conversion

    -

    考虑把HFOV转化为VFOV,假设我们知道了近平面距离和aspect -ratio,与HFOV

    -

    我们定义变量alphabeta: -

    alpha = HFOV / 2
    beta = VFOV / 2
    tan (alpha) = (viewport width / 2) / near plane distance
    tan (beta) = (viewport height / 2) / near plane distance

    -

    从而有:

    -
    tan (alpha) * near plane distance = viewport width / 2
    Near plane distance = (viewport width / 2) / tan (alpha)
    tan (beta) * near plane distance = viewport height / 2
    Near plane distance = (viewport height / 2) / tan (beta)
    -

    进而有:

    -
    (viewport width / 2) * tan (beta) = (viewport height / 2) * tan (alpha)
    tan (beta) = [(viewport height / 2) / (viewport width / 2)] * tan (alpha)
    tan (beta) = aspect ratio * tan (alpha)
    VFOV = 2 * arctan (tan (HFOV / 2) * aspect ratio)
    -

    所以,只要知道了aspect ratio和其中一个FOV,就可以求出另一个FOV

    -

    Quaternions

    -

    四元数的好处在于可以很方便地插值,且没有Gimbal Lock问题。

    -

    Bump and Ease Functions

    -

    Bump和ease -functions都用来保证开始和结束的平滑,ease函数分为ease-in和ease-out函数,ease-in函数也叫damping函数。下面讨论几个简单的函数来生成S型曲线

    -

    Exponentials

    -

    使用简单的三次函数:

    float exp = 3t^2 - 2t^3;

    -

    Proportional

    -

    Proportional ease -function使用当前值和期望值之间的差值作为输入,常用一个damping range: -

    float deltaValue = desiredValue - currentValue;
    if (fabs(deltaValue) < kRange>)
    {
    float factor = Math::Limit (deltaValue / kRange, 1.f);
    currentValue += kSpeed * factor * deltaTime;
    }

    -

    Proportional damping -function实现简单且高效,但是可能不如其他方法平滑。另一种方法是根据经过的时间计算factor: -

    float const dampingFactor (1.0f - elapsedTime / totalTime);
    interpolant += (destinationValue - interpolant) * dampingFactor * deltaTime;
    return interpolant;

    -

    Spherical linear -interpolation

    -

    球面线性插值

    -

    Transcendentals

    -

    用三角函数:

    -
    angle = (pi * time factor) - (pi / 2);  // in radians, -pi/2 ... pi/2
    time factor = (sinf(angle) + 1) / 2
    -

    Springs

    -

    当实现相机移动的时候我们常常碰到相机overshoot的问题,这是因为目标在不断移动,阻止相机足够快地改变方向、速度以避免被目标对象自身抢占位置,但同时我们也不想相机过快移动,这就导致了矛盾

    -

    此时我们可以使用弹簧方案,基于下述的方程:

    -

    -

    是弹性常量,一个正数表示弹簧的弹性,是弹簧伸缩度,这样我们可以写出下面的伪代码: -

    Vec3 deltaPosition = desiredPosition - currentPosition;
    Vec3 movementDirection = deltaPosition.AsNormalized ();
    float extension = deltaPosition.Magnitude ();
    float force = -kSpringConstant * extension; // for a unit mass, this is acceleration, F = ma
    Vec3 newVelocity = currentVelocity + (movementDirection * force);
    new Position = currentPosition + (currentVelocity * deltaTime);

    -

    弹簧方案的一个问题是设置弹簧的弹性常量,并且会出现震荡现象,这可以用两种思路解决:

    +

    Scripting Tools

    +

    World editor support

    +

    必须支持script objects的放置与操作,而且还要支持message passing, +state transition, event trigger和其他物体间的关系

    +

    Communication +between target platform and development PC

    +

    要获得运行物体的状态

    +

    Messaging/event logging

    +

    对消息和事件进行记录

    +

    Object properties debugging

    +

    当游戏运行时要动态检查物体的属性

    +

    Replay

    +

    最简单的方法是内置video recording

    +

    Console window

    +

    支持打印文本消息,通过GM指令查看想要的文本消息

    +

    Scripting Debugging

    +

    常见的debugging方法:

      -
    • 使用damped spring,控制施加的力使得弹簧接近但永远不会超过目标
    • -
    • 使用feedback controller
    • +
    • Script statement execution/filter: +对任意给定的script,展示每个状态执行的历史记录
    • +
    • Debug message logging: 单独展示日志
    • +
    • Message filtering: 增加过滤功能以更好地debug
    • +
    • Object state: 展示物体的状态
    • +
    • FSM state changes: 展示当前的state和历史state
    • +
    • Animation state and event management: +记录当前的动画状态和插值状态,记录事件
    -

    Digital Filters

    -

    在控制器输入或相机移动中,我们想要移除小的震荡,此时可用滤波器

    -

    Low pass

    -

    低通滤波让低频信号通过,而过滤高频信号

    -

    High pass

    -

    与低通滤波相反

    -

    Band

    -

    移除某个频率范围之外的信号

    -

    Finite impulse response

    -

    有限脉冲响应滤波在计算当前值的时候结合之前的输入值,因为易于实现,所以与IIR滤波相比更倾向于FIR滤波

    -

    History -buffer中的每一项都有一个对应的滤波系数,对应了该项目的影响有多大,即改变对输入值的响应度

    -
    static float const firCoefficients[] = 
    {
    // these values greatly influence the filter
    // response and may be adjusted accordingly
    0.f, 0.f, 0.f, 1.f, 2.f, 3.f, 4.f
    };
    float CFIRFilter::Initialize (void)
    {
    // setup the coefficients for desired response
    for (int i = 0; i < mHistoryBuffer.size(); ++i)
    mCoefficients[i] = firCoefficients[i];
    }
    float CFIRFilter::Update (float const input)
    {
    // copy the entries in the history buffer up by one
    // position (i.e. lower entries are more recent)
    for (int i = mHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mHistoryBuffer[i + 1] = mHistoryBuffer[i];
    }
    mHistoryBuffer[0] = input;
    float fir(0);
    // now accumulate the values from the history
    // buffer multiplied by the coefficients
    for (int i = 0; i > mHistoryBuffer.size(); ++i)
    {
    fir += mCoefficients[i] * mHistoryBuffer[i];
    }
    return fir;
    }
    -

    Infinite impulse response

    -

    不同于FIR,IIR也加入了之前的输出值

    -
    float CIIRFilter::Initialize (void)
    {
    mInputCoefficients[0] = 0.5f;
    mInputCoefficients[1] = 0.3f;
    mOutputCoefficients[0] = 0.5f;
    mOutputCoefficients[1] = 0.3f;
    }
    float CIIRFilter::Update (float const input)
    {
    for (int i = mInputHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mInputHistoryBuffer[i + 1] = mInputHistoryBuffer[i];
    }
    mInputHistoryBuffer[0] = input;
    float const result =
    mInputCoefficients[0] * mInputHistoryBuffer[0] +
    mInputCoefficients[1] * mInputHistoryBuffer[1] +
    mOutputCoefficients[0] * mOutputHistoryBuffer[0] +
    mOutputCoefficients[1] * mOutputHistoryBuffer[1];
    for (int i = mOutputHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mOutputHistoryBuffer[i + 1] = mOutputHistoryBuffer[i];
    }
    mOutputHistoryBuffer[0] = result;
    return result;
    }
    -

    Spline Curves

    -

    Spline由一系列控制点和对应的tangent -vector控制,每个segment通常由两个控制点组成,每个控制点有1~2个切线向量。相邻两个segment之间共享的控制点叫做joint

    -

    Camera spline usage

    -

    相机Spline的通常用法是定义一条路径,再用一个evaluation函数将时间映射到路径上的位置。二维spline通常用来表示物体的属性,三维spline通常用来表示空间位置

    -

    Cubic polynomials

    -

    三次多项式可以表示为:

    -

    -

    有时候需要给定解出

    -

    Spline types

    -

    下面介绍几种不同的spline types:

    +

    Part 3: Camera Engineering

    +

    Chapter 7: Position and +Orientation

    +

    Coordinate Schemes

    +

    有几种不同的坐标空间:

      -
    • Linear: 一系列直线,用下面的代码计算位置
      Vec3 const Linear (Vec3 const & a, Vec3 const & b, float const time)
      {
      return a + ((1.f - time) * Vec3 (b - a));
      }
    • -
    • Piecewise Hermite: -Hermite曲线要求两个控制前和两个切线,每个控制点一条切线。我们也可以为每个控制点引入两条切线——in-tangent和out-tangent,很多动画系统用这种方法 -
      const Vec3 Hermit (Vec3 const & a, Vec3 const & b, Vec3 const & startTangent, Vec3 const & endTangent, float const time)
      {
      if (u <= 0.0f)
      return a;
      else if (u >= 1.0f)
      return b;

      float const t2 (time * time);
      float const t3 (t2 * time);

      // Calculate basis functions
      float const a0 = (t3 * 2.0f) - (3 * t2) + 1.0f;
      float const a1 = (-2.0f * t3) + (3.0f * t2);
      float const b0 = t3 - (2.0f * t2) + u;
      float const b1 = t3 - t2;

      // Use cubic basis functions with points and tangents
      Vec3 const result ((a0 * a) + (a1 * b) + (b0 * startTangent) + (b1 * endTangent));
      return result;
      }
    • -
    • Catmull-Rom: Catmull-Rom spline -C1连续、局部控制,但是并不位于控制点形成的convex hull中 -
      Vec3 const CatmullRom (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
      {
      if (time <= 0.0f)
      return b;
      if (time >= 1.0f)
      return c;

      float const t2 = time * time;
      float const t3 = t2 * time;

      Vec3 const result = (a * (-0.5f * t3 + t2 - 0.5f * time) +
      b * (1.5f * t3 - 2.5f * t2 + 1.0f) +
      c * (1.5f * t3 + 2.0f * t2 + 0.5f * time) +
      d * (0.5f * t3 - 0.5f * t2));
      return result;
      }
    • -
    • Rounded Catmull-Rom: 能够决定两个控制点中间曲线的速度 -
      Vec3 const RoundedCatmullRom (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
      {
      if (time <= 0.0f)
      return b;
      if (time >= 1.0f)
      return c;

      // find velocities at b and c
      Vec3 const cb = c - b;
      if (!cb.IsNormalizable ())
      return b;
      Vec3 ab = a - b;
      if (!ab.IsNormalizable ())
      ab = Vec3 (0, 1, 0);
      Vec3 bVelocity = cb.AsNormalized () - ab.AsNormalized ();
      if (bVelocity.IsNormalizable ())
      bVelocity.Normalize ();
      else
      bVelocity.Vec3 (0, 1, 0);

      Vec3 dc = d - c;
      if (!dc.IsNormalizable ())
      dc = Vec3 (0, 1, 0);
      Vec3 bc = -cb;
      Vec3 cVelocity = dc.AsNormalized () - bc.AsNormalized ();
      if (cVelocity.IsNormalizable ())
      cVelocity.Normalize ();
      else
      bVelocity = Vec3 (0, 1, 0);

      float const cbDistance = cb.Magnitude ();
      return CatmullRom (b, c, bVelocity * cbDistance, cVelocity * cbDistance, time);

      }
    • -
    • Kochanek-Bartels splines: KB spline是CR -spline的扩展,因为引入了其他的参数来控制spline的曲率。有三个参数: +
    • World Space: 从世界空间到相机空间再到屏幕空间
    • +
    • Camera Space: 以相机为原点
    • +
    • Screen Space: Camera Space通过正交变换而来
    • +
    • Local Space: 以角色为原点
    • +
    • Object Relative: 相机为原点,三个轴根据某个物体到相机的位置而定
    • +
    +

    Desired Position

    +

    在相机移动的过程中需要关注与目标点的距离

    +

    First person camera +positioning

    +

    大多数情况下,相机的desired +position就是角色眼睛的位置,此时增加一点dvertical +amping会有用。在玩家遇到崎岖地形或与物体碰撞时,需要smooth +out轻微的相机运动

    +

    Third person camera +positioning

    +

    有三种策略决定相机位置:

      -
    • Bias: -控制每个tangent的方向,-1使曲线提早buckle,+1使曲线buckle到末尾
    • -
    • Tension: 控制每个tangent vector的长度, -+1导致更紧的曲线,-1导致更圆的曲线,如果大于+1则产生环
    • -
    • Continuity: -控制tangents之间的角度,-1使曲线向里buckel,+1使边角朝向相反方向 -
      Vec3 const KBSpline (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time,
      float const tension, float const continuity, float const bias)
      {
      // tension, continuity ans bias defined per segment
      Vec3 const ab = Vec3 (b - a).AsNormalized ();
      Vec3 const cd = Vec3 (d - c).AsNormalized ();
      Vec3 const inTangent = ((1.f - tension) * (1.f - continuity) * (1.f + bias)) * 0.5f * ab +
      ((1.f - tension) * (1.f + continuity) * (1.f - bias)) * 0.5f * cd;
      Vec3 const outTangent = ((1.f - tension) * (1.f + continuity) * (1.f + bias)) * 0.5f * ab +
      ((1.f - tension) * (1.f - continuity) * (1.f - bias)) * 0.5f * cd;
      return PiecewiseHermite (b, c, inTangent, outTangent, time);
      }
      如果所有参数都为零,则退化为CR spline
    • -
    -
  • Bézier: 二次贝塞尔曲线方程为
  • +
  • Automated: 相机完全自动化
  • +
  • Player control: 交由玩家操作相机,但要让环境始终保持在视野中
  • +
  • Hybrid: 交给玩家部分自主权
  • -

    -
    Vec3 const QuadraticBezier (Vec3 const & a, Vec3 const & b, Vec3 const & c, float const time)
    {
    float const oneMinusTime (1.f - time);
    Vec3 const bezier = (a * oneMinusTime * oneMinusTime) + (b * 2.f * time * oneMinusTime) + (c * time * time);
    return bezier;
    }
    -

    三次贝塞尔曲线方程为:

    -

    -
    Vec3 const CubicBezier (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
    {
    float const oneMinusTime (1.f - time);
    Vec3 const bezier = (a * oneMinusTime * oneMinusTime * oneMinusTime) + (b * 3.f * time * oneMinusTime * oneMinusTime) + (c * 3.f * time * time * oneMinusTime) + (d * time * time * time);
    return bezier;
    }
    +

    Desired Position +Determination Methods

    +

    相机位置应该独立于朝向,尽管后者常常依赖于前者。下面是一些决定desired +position的方法

    +

    Stationary

    +

    主要有两种stationary cameras: fixed position 和 dynamic fixed +position:

      -
    • Uniform cubic B-spline: B spline是一种泛化的Bezier -spline,一个好处是它有local -control,即:每个控制点只会影响整个曲线的一小部分,它使用下述的blending -function
    • +
    • Fixed position: +相机位置固定但可以自由调整朝向,通常限制了朝向的范围。但此类相机会使目标离相机太远或者丢失视野
    • +
    • Dynamic fixed position: 通常由其他活跃的相机决定,中间由jump +cut或interpolation决定
    -

    -
    Vec3 const BSpline (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
    {
    float const t2 (time * time);
    float const t3 (t2 * time);

    Vec3 const result ((a * (-t3 + (3 * t2) + (-3 * time) + 1)) +
    (b * ((3 * t3) + (-6 * t2) + 4)) +
    (c * ((-3 * t3) + (3 * t2) + (3 * time) + 1)) +
    (d * t3));
    return (result / 6.0f);
    }
    +

    Slaved/tracking

    +

    相机的offset可以采取以下的几种形式:

      -
    • Non-uniform rational B-spline
    • +
    • World-relative offset: 在世界坐标内保持相对距离
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • +
    • World-relative angular offset: 在世界坐标中通过角度定义相对距离 +
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      // construct a rotation matrix around the world up-axis
      Mat3 rotation = Mat3::ZRotation(yaw);
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • +
    • World object relative: +根据主物体和另一个物体之间的距离决定相机位置,这种方法可以帮助处理人物在相机和另一物体中间的情况 +
      Mat3 rotation = Mat3::LookAt(GetTargetObject()->GetPosition().DropZ(), mWorldPosition.DropZ());
      // note that the reference frame is often 2D, but is not required to be
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • +
    • Local offset: 与world relative +offset类似,只不过offset被转换到了局部空间
      Mat3 rotation = mScriptObject->GetMatrix(); // based on the orientation of the script object
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • +
    • Local angualar offset: 类似world-relative angular +offset,不过offset被转换到了局部空间
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      Mat3 rotation = mScriptObject->GetMatrix();
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    • +
    • Character-relative offset: +在局部坐标内根据角色的朝向决定相机的位置,在三人称相机中最为常用,被称为“追背相机 +(chase camera)”, 一般不会变化Roll
    • +
    • Character-relative angular offset: +在局部坐标内根据角色朝向和三个旋转角度、距离决定相机位置,通常会限制相机旋转速度的变化率
    • +
    • Object-relative offset: +不考虑目前物体的朝向,而仅考虑elevation和distance,the vector from the +target object toward the current camera position defines the coordinate +space used to calculate the desired position,相机的朝向通常由玩家操纵 +
      Mat3 rotation = Mat3::LookAt(GetPosition().DropZ(), GetTargetObject()->GetPosition().DropZ(), mWorldPosition);
      // only elevation (here pitch) and distance are required
      Vec3 offset(0.0f, cosf(pitch) * distance, sinf(pitch) * distance);
      mOffset = rotation * offset;
      Vec3 desiredPosition = GetTargetObject()->GetPosition() + mOffset;
    -

    Continuity

    -

    C0是位置连续,C1是速度连续,C2是加速度连续。所以C1或更高的连续会在视觉上效果更好,C2或更高的连续会有更平滑的移动

    -

    Spline definitions

    -

    为了充分地定义spline curve,我们需要几个机制:

    +

    Path

    +

    有几种不同类型的路径:

      -
    • 一个能够在游戏世界放置控制点的编辑器,甚至可以在游戏运行的时候生成
    • -
    • Spline evaluation type
    • -
    • 跨国spline的总的时间或一个映射函数
    • -
    • 一个可视化展示spline curve的界面,随参数改变动态变化
    • +
    • Linear: 直线路径
    • +
    • Circular/elliptical/spiral: 环状路径
      X = a * cos(theta);
      Y = b * sin(theta);
    • +
    • Spline: 通常一系列control points定义
    -

    Spline evaluation

    -

    可以预先计算整个spline的长度,然后用一个线性长度作为spline上的位置。如果再搭配速度damping则会产生比较平滑的效果

    -

    Control point generation

    +

    下面是决定路径位置的方法:

      -
    • 单个控制点:总是返回这个点
    • -
    • 两个控制点:要提供额外的tangent信息,否则无法确定曲线
    • -
    • 三个控制点:可以外推第四个控制点
    • +
    • Non-interactive situations: 在movie +sequences中通常由一个时间到位置的映射决定
    • +
    • The position of the player character relative to another object
    • +
    • Player position relative to a defined path
    • +
    • A specified distance away from the closest position on the path to +the player: 改方法需要注意相机移动的平滑性
    -

    Parameterized arc length

    -

    积分法

    -

    Total spline length

    -

    总长度是各个spline弧长度的和,往往采用数值方法近似曲线长度

    -

    Closest position

    -

    计算spline上离某个位置最近的点往往很有用,但问题是可能有多个解,这时候就需要对这些解做个排序,比如考虑相机的前一个位置或者LOS,或者相机的角速度

    -
    Determine the nornal at each end of the segment and by using similar triangles
    Determine the length on the segment
    If length within 0..1 then
    Convert linear 0..1 into a parametric value
    With parametric value 0..1 find the position within the arc using the usual spline evaluation
    Check position against source position for LOS and other factors
    If OK, determine the physical distance between two points and compare to current "best"
    Else
    Not within segment, so proceed to next segment
    -

    Spline editing

    -

    开发能够编辑spline的工具

    -

    Interpolation

    -

    线性插值在一对数据点中生成新数据,piecewise插值则是由几个连续的数据点去生成。相机朝向往往用非线性插值

    -

    Camera Property -Interpolation

    -

    Position interpolation

    -

    位置插值应该是基于当前的位置和目标位置,用这个距离相比于原来的距离

    -
    Vec3 interpFactor = CMath::Clamp (0.0f, interpTimer/maxInterpTime, 1.0f);
    Vec3 currentDeltaPosition = desiredPosition - GetTranslation ();
    Vec3 newPosition = GetTranslation () + (currentDeltaPostion * interpFactor);
    // when interpTimer reaches maxInterpTime, the camera reaches the destination
    -

    Orientation interpolation

    -

    朝向插值通常不涉及roll,即使需要考虑roll,也会单独处理

    -

    由于小的朝向改变会引起巨大的视觉变化,所以一定要确保朝向插值的平滑。线性插值并不保证对象一定在视野里。如果朝向改变很大,要限制角速度,或者使用jump -cut。朝向插值也要保证路径最短

    +

    Splines. 可以用brute +force的方法去计算spline的长度,代码如下

    float length(0.0f);
    for (int i = 0; i < controlPoints.Size(); ++i)
    {
    Vec3 pathPosition = EvaluateSegment(i, 0.0f);
    // start of i'th segment
    controlPoints[i].mLength = length;
    // save length at the control point
    for (int j = 1; i < kMaxSegmentSlices; ++j)
    // includes next control point
    {
    Vec3 newPosition = EvaluateSegment(i, j/kMaxSegmentSlices);
    Vec3 delta = newPosition - pathPosition;
    length += delta.Magnitude();
    // add the "length" of this slice
    pathPosition = newPosition;
    }
    } // length now holds the approximate total length

    +

    一种更快且准确的方法是用多项式逼近

    +

    Mapping functions. +最简单的是从时间到长度的线性映射,此时相机的速度不变,导致abrupt开始或结束。一种常见的方法是使用一个二维spline曲线(Hermite +curves),用户可以定义控制点和切线控制曲线的形状

    +

    Surface constrained

    +

    在一些情况下,相机可能限制在一个隐式的平面上,比如一个flat平面,一个椭球、圆柱等等,该平面的大小和朝向可能是动态的

    +

    下面是一些常见的surface类型:

      -
    • Orientation interpolation by angle: NLerp, Slerp, -Squad。基于角度的插值的一个缺点是:相机可能会看得远离目标物体,取决于变化的速度。这可能发生在原相机和目标相机都看向同一点,此时在插值的过程中相机会偏离目标点
    • -
    • Orientation interpolation by target position: -可以用基于目标位置的插值保证目标始终在视野内,但此时朝向速度的变化非常重要
    • -
    • Orientation interpolation characteristics: -当前朝向和期望朝向之间的角度要随着插值越来越小,可以通过两个方法实现:(1)反向计算从目标朝向到当前朝向;(2)保证当前插值角小于等于前一步插值角,如果因为目标朝向改变导致角度增大,则使用前一个插值相机的朝向,一旦插值相机朝向接近目标朝向,则把朝向锁定
    • -
    • Roll interpolation: -用最短方向插值roll,但一般来说roll只用于短时间的相机效果
    • +
    • Sphere: 容易受gimbal lock影响
    • +
    • Plane
    • +
    • Cylinder
    • +
    • Cone
    • +
    • Extruded spline plane
    • +
    • Spline cylinder
    -

    FOV interpolation

    -

    如果没有其他渲染效果,那么只改变FOV就显得很突兀,快速的FOV改变会让玩家不适,这时候一个jump -cut反而更好。zoom in也是通过FOV实现的

    -
    float targetFOV = GetTargetFOV ();
    float deltaFOV = mFOV - targetFOV;
    float newFOV = targetFOV + (deltaFOV * deltaTime);
    float newDeltaFOV = newFOV - targetFOV;
    if (absF(newDeltaFOV) < absF(deltaFOV))
    {
    mFOV = newFOV;
    }
    -

    Viewport Interpolation

    -

    改变显示设备的大小,通常用于cinematic sequences和regular game -play之间的转换。比如从2.35:1到1.33:1

    -

    Player Control Interpolation

    -

    First person cameras

    -

    control interpolation通常用于从一个第一人称control reference -frame变化到一个fixed control reference frame比如object orbiting

    -

    Third person cameras

    -

    在很多情况下,我们要保证如果相机出现巨大移动,则玩家控制不会马上改变

    -

    一个问题是如何处理玩家移动比相机移动快的问题,而且玩家绕着相机垂直轴的任何移动会导致相机绕着该轴的快速转动

    -

    Interpolation Choices

    -

    首先要问现有的数据是kay value还是control -values;第二,要考虑插值方法的效率和效果;第三,要考虑插值结果的平滑性;最后,也要考虑不同插值方法需要的数据量

    -

    Linear interpolation

    -

    线性插值只需要三个参数:source value, destination -value和interpolation method

    -

    Destination value往往会随时间改变,我们就需要考虑到destination -value的变化率,当插值的变化率不匹配目标值的变化率时,不连续就会发生

    -

    插值方法需要考虑是否要用固定的时间完成插值。不考虑变化率会在开始和结束时产生不连续现象

    -

    Piecewise interpolation

    -

    通常有多于两个数据点进行插值,一种策略是把它们视为独立的interpolation -segments:

    bool foundTime (false);
    for (int i = 0; i < valueTimes.Size() - 1; ++i)
    {
    if (valueTimes[i+1] > time)
    {
    float timeDelta = valueTimes[i+1] - valueTimes[i];
    timeFactor = (time - valueTimes[i]);
    segment = i;
    source = values[i];
    destination = values[i+1];
    foundTime = true;
    break;
    }
    }
    if (foundTime)
    return Interpolate (source, destination, timeFactor);

    -

    此外,也可以在每对数据点中假设均匀时间区间,但会有比较大的问题

    -

    Methods of Interpolation

    -

    Linear time interpolation

    -
    Interpolated value = source + ((destination - source) * t)
    -

    Parametric functions

    -

    比如ease函数

    -

    Spherical linear -interpolation

    -

    用于角的插值

    -

    Potential Interpolation -Problems

    -

    Aesthetic problems

    -

    一些视觉上的问题包括:

    +

    Volume constrained

    +

    相机与圆柱体的碰撞检测:

    +
    Vec3 direction = currentPosition - cylinder.GetPosition();
    direction.SetZ(0.0f);
    float radius = direction.Magnitude();
    if (radius > kMaxRadius)
    radius = kMaxRadius;
    direction = direction.AsNormalized() * radius;
    Vec3 newPosition = cylinder.GetPosition() + direction;
    newPosition.SetZ(currentPosition.GetZ());
    +

    Framing

    +

    该方法在屏幕空间上保持目标角色的位置,这可以通过移动相机、修改FOV、伸缩viewport等方法实现

    +

    Object-framing relative

    +

    该方法不仅考虑物体在屏幕空间中的小,而且也考虑它和另一个物体之间的相对距离,一些因素包括:

      -
    • Geometry interpenetration
    • -
    • Discontinuities
    • -
    • Interpolation noise
    • -
    • Target object framing
    • -
    • Large orientation changes
    • -
    • Large interpolation distances
    • +
    • Object screen size
    • +
    • Multiple objects must be kept on screen
    • +
    • Distance between objects
    • +
    • Relative position to other objects
    • +
    • Relative position to the screen (not rendered size)
    • +
    • Orientation with respect to the camera
    • +
    • Distance aiming/ranged weapons
    • +
    +

    Axis rotational/spindle

    +

    相机围绕游戏世界中的一个轴旋转、平移,适用于玩家需要围着一个物体导航的情况,或限制在一个圆柱物体的情况

    +

    一些影响相机位置的因素:

    +
      +
    • Angular offset relative to the reference vector or the axis +origin
    • +
    • Axial offset relative to the current player positionn or the axis +origin
    • +
    • Radial offset relative to the spindle axis or current player +radius
    -

    Mathematical problems

    -

    数学上的问题一般是由浮点误差导致的:

    +

    此外,我们也可以改变相机的朝向:

      -
    • Co-linear (or exactly opposed) orientation
    • -
    • Coincident positions: 0为除数
    • -
    • Floating-point inaccuracy
    • +
    • Look at the spindle axis along a projected vector from the current +camera position
    • +
    • Look away from the spindle along a projected vector that passes +through the camera position as above
    • +
    • Apply an angular offset either toward or away from the spindle +relative to the target object
    -

    Interruption of -Interpolation

    -

    有可能在一个插值过程中触发了另一个插值,这时候可以从当前值开始直接开始新的插值

    -

    Transitions

    -

    Position during transitions

    -

    Orientation during -transitions

    -

    有两种方法在transition的过程中控制插值相机的朝向,如果source和des相机没有reorienting,那么简单的线性插值或slerp可以处理得很好,但如果有一个或者两个都reorient就会有问题。一个方法是计算从目标值到当前值之间得剩余量(比如角度),如果新的插值大于当前值,则忽略

    -

    Camera Math Problems

    -

    Floating-point precision

    -

    相机系统一般用单精度浮点数,所以不能表示很大的数

    -

    Epsilon usage

    -

    要根据用途改变epsilon的值

    -

    Compiler differences

    -

    编译器也有不同

    -

    Hardware FPU differences

    -

    Vector normalization

    -

    Matrix concatenation -floating-point drift

    -

    在应用旋转矩阵后,正交化该矩阵以保证浮点精确度

    -

    Periodic Camera Mathematical -Fixes

    -

    最好在渲染之前检查当前的相机变换是不是正确的,包括:

    +

    玩家位置相对于spindle axis的半径可以作为mapping +function的输入,当玩家移动半径更小时,相机移动得更高

    +

    Common Position Problems

      -
    • 变换矩阵的每个元素不包含异常值,比如NaN
    • -
    • 变换矩阵的每个向量都是单位向量
    • -
    • Up-vector和游戏内的Up-vector一致
    • -
    • 必要时移除roll
    • -
    • 必要时限制相机快速的reorientation
    • +
    • 期望位置和一个物体碰撞,或者太近以至于物体穿透相机的近平面
    • +
    • 期望位置离环境中的边界物体太近以至于有环境外的视野
    • +
    • 期望位置离目标对象太远,玩家没有好的视野
    • +
    • 环境复杂相机不能提供好的视野
    -

    如果矩阵不能被修复,则使用上一帧的矩阵;记得多正交化矩阵

    -

    Chapter 11: Implementation

    -

    Game Engine Architecture

    -

    Game update loop

    -

    大多数相机逻辑都要在其他逻辑之后执行,典型的执行顺序是:

    +

    Orientation

    +

    旋转有四种表示方法:欧拉角、变换矩阵、轴角和四元数

    +

    Desired Orientation +Determination Methods

    +

    Constant orientation

    +

    朝向不变,但是位置可以移动。可以变式为constant elevation +cameras,即只有pitch可以改变

    +

    这种相机可以用在:

      -
    • Process input
    • -
    • Process pre-logic on game objects
    • -
    • Process main logic (AI, etc.) on game objects
    • -
    • Move game objects and resolve collisions
    • -
    • Process all camera logic
    • -
    • Process post-camera logic for objects (such as dependencies on -camera position, orientation, etc.)
    • -
    • Process all viewport logic
    • -
    • Render scene for each viewport
    • +
    • Player controlled remote cameras
    • +
    • Dynamic positioning of replay cameras
    • +
    • Dynamic positioning of caemras for non-interactive game play +sequences
    -

    Game system managers

    -

    一些管理系统包括:

    +

    Tracking a target object +or position

    +

    相机达到期望朝向的方法取决于目标物体的移动和相机的重朝向速度,此类相机一般保持固定的pitch

    +

    Look-at offset

    +

    许多三人称游戏使用一个固定的look-at offset

    +

    Locked look-at position

    +

    玩家的朝向也会随便改变

    +

    Target object position +prediction

    +

    Object framing

    +

    一个很好的例子是格斗游戏,双方玩家都希望自己和对方都能呈现在屏幕上,当玩家距离增加的时候,相机要么拉远,要么增大FOV,但是频繁切换FOV通常不可取

    +

    回放相机通常会从不同的视角呈现玩家行为

    +

    Idle wandering

    +

    A semi-random camera reorientation while the player character is +idle

    +

    Automated orientation +control

    +

    在没有玩家操作的情况下自动帮助相机转向:

      -
    • Camera manager: -跟踪所有的相机,传递输入、确保每个都得到正确更新
    • -
    • Object manager: 管理游戏中的每个对象,保证逻辑处理顺序正确
    • -
    • Message manager
    • -
    • Audio manager: 通过它控制音效的变化
    • -
    • Input manager: 将输入信息传递给所有需要的游戏对象
    • +
    • Automated control over camera pitch when the player is jumping: +从玩家起跳时开始相机可以向下看
    • +
    • Automated pitch control when traversing environmental features: This +is applied to present a view facing up or down a ramp, staircase or +other incline as appropriate so that players have a better view of what +they are moving toward, +在有洞穴或悬崖的地方,相机应该自动看向以给予提示
    • +
    • Automated pitch control during combat or interactions: +自动调整pitch以提示可交互的物体
    • +
    • Automated reorientation of the player or a camera toward a target +position: 锁定到目标物体
    • +
    • Repositioning and reorientation of the camera to face the same +direction as the player character
    • +
    • Transitions from first to third person cameras: +要保证当相机移动地充分远的时候才渲染物体,避免穿帮,此时可通过fade角色解决此问题
    • +
    • Transitions from third to first person cameras: 可通过cut +transition实现
    -

    Delta time

    -

    现在游戏通常将逻辑层与渲染层分开,这有利于CPU处理和GPU渲染,游戏逻辑可能以固定的速率更新,也可能以动态的速度更新,从而,相邻两次更新的时间间隔不一定一致,经过的时间就称为delta -time

    -

    Input processing

    -

    很多相机需要把输入值传递给它们,这通常由相机管理器实

    -

    Camera System Architecture

    -

    Viewport manager

    -

    主要工作是包含所有在场景内需要渲染的东西,处理控制器输入、渲染、masking、buffer管理、aspect -ratio等等,能够被其他游戏系统用于访问关于当前活跃相机的相机系统。数据结构是:

    -
    class CViewportManager
    {
    public:
    typedef int32 TViewportHandle;
    TViewportHandle const CreatViewport (EViewportType);
    void DeleteViewport (TViewportHandle const viewport);
    void Update (float const deltaTime);
    void ProcessInput (CInput const & input);
    CViewport const & GetViewport (TViewportHandle const handle) const;
    }
    -

    在定义了显示媒介之后,我们需要描述投影,因此VM需要:

    +

    Reorientation Methods

    +

    Applying rotations

    +

    有几种方法去应用旋转:

      -
    • Accessors for viewports
    • -
    • Render all viewports
    • -
    • Input handler
    • -
    • Create a new viewport
    • -
    • Activate or deactivate an existing viewport
    • -
    • Delete an existing viewport
    • -
    • Transition (morph) between two viewports
    • -
    • Change viewport properties
    • -
    • Assign an active camera for a viewport
    • +
    • Constant angular velocity: +匀速变动旋转角,但是开始和结束会有突变
    • +
    • Acceleration and deceleration: 应用加速度与减速度
    • +
    • Angular velocity damping: 用damp使开始结尾更皮规划
    • +
    • Free-look damping: 用bump和其他的ease +functions防止因noise导致的相机朝向变动
    • +
    • Twist reduction
    -

    每个Viewport都控制了所有需要渲染的信息,比如相机、控制器输入、surface -locatio and -size、渲染模式等等,甚至还包含了用于多种输出设备渲染需要的信息

    -

    VM还处理不同viewport之间的过渡,通常用于玩家在菜单界面、暂停时,或者画中画。Transition可能包括:

    +

    Reorientation lag

    +

    通常我们希望相机朝向的变化能有一些延迟(lag),同时保持平滑

    +

    很多游戏没有任何延迟,这有两个影响:第一,相机直接看向角色会导致玩家不能提前看见角色的移动,第二,相机非常依赖角色的移动,任何小的扰动就会影响相机表现

    +

    因此需要使小的移动没有影响,并且使用damp。当插值的时候,可以限制每帧允许的旋转角度。此外,lag还可以通过用springs或feedback +controller实现

    +

    Offsets

    +

    一个常被忽略的点是look-at position和target +object之间的关系。如果直接看向角色,那么就容易丢失玩家想要看的东西。可以用一些视觉提示帮助玩家瞄准目标对象,比如highlight敌人,或者lock-on +to the target object

    +

    Smoothing and damping

    +

    常用的平滑方法是limit the angular velocity of the camera +proportionally to the angle between the current and desired +orientations,这可以表示为一个简单的三角函数:

    +
    Real32 const kDampingAngle(30.0f * gkRadiansPerDegree);
    Real32 angle = acosf(CVector3f::Dot(currentOrientation, desiredOrientation));
    Real32 const kDampingFactor = Cmath::Limit(angle / kDampingAngle, 1.0f); // linearly proportional
    Real32 angularLimit = angularSpeed * deltaTime * kDampingFactor;
    CQuaternion newOrientation = CQuaternion::LookAt(currentOrientation, desiredOrientation, angularLimit);
    +

    始终记住我们有两个矛盾的要求:平滑移动和保持角色合理视角

    +

    Springs and PID controllers

    +

    In practice, tuning the characteristics of the controller to achieve +this behavior can be time-consuming but very worthwhile

    +

    Free-Look

    +

    First person free-look

    +

    很多一人称游戏采用circle +strafing技术允许玩家free-look,有些游戏也只允许垂直方向的朝向变化,当然也要限制最大角度

    +

    Third person free-look

    +

    用一个圆锥体围绕look-at点,并使用一个弹簧在没有输入指令的时候令相机能够回到初始位置

    +

    Free-look orientation +determination

    +

    决定free-look朝向通常有两种方法:self-centering和non-centering

      -
    • Cut
    • -
    • Wipe
    • -
    • Cross fade
    • -
    • Morphing
    • +
    • Self-centering free-look: +玩家输入会让相机朝向改变,通常把遥感值映射为相机朝向的速度
    • +
    • Non-centering free-look: 在没有玩家输入时相机会保持现在的朝向
    -

    Render manager

    -

    从当前的viewports和cameras获取数据并渲染,此外会移除frustum外的的物体,决定渲染顺序,应用后处理

    -

    Camera manager

    -

    CM的主要责任是扮演所有相机的控制器,生成相机新实例、处理转场、插值、优先级、控制器输入、replay -modes和时间控制

    -

    CM有Update函数,处理步骤是:

    +

    Common orientation problems

    +

    一些常见的相机朝向的问题:

      -
    • 决定哪个相机需要开始或结束
    • -
    • 更新活动相机的逻辑(插值相机最后)
    • -
    • 传递控制器输入
    • -
    • 更新audio系统的位置和速度信息
    • -
    • 设置渲染环境
    • +
    • Gimbal lock: 当相机forward与世界up +axis平行时发生,要么不让相机平行于up axis,要么用四元数构造变换矩阵
    • +
    • Vertical twist: 当使用三人称相机时,如果相机朝向接近平行于up +axis则会出现这个问题,表现为相机快速围绕up +axis旋转,常发生于相机在墙角的情况,此时可以限制相机围绕up +axis的旋转速度,或者禁止相机朝向改变直到相机离玩家足够远
    • +
    • Roll: 飞行模拟游戏会经常使用roll
    • +
    • Orientation noise: 使用high-pass filter移除不想要的朝向变化
    • +
    • Rapid orientation changes: +当允许玩家快速移动/改变朝向,或玩家在相机下方或上方移动时,相机的朝向就会快速改变,带给玩家糟糕的体验
    • +
    • Frustum culling of the player character: +三人称游戏必须首先考虑frame玩家,切通常相机直接看向玩家前进的方向,但也要让玩家保持在view +frustum中
    -

    Camera update loop

    -

    相机一般是下面的更新逻辑:

    +

    Chapter 8: Navigation and +Occulusion

    +

    Dynamic determination of how the camera should reach its desired +position is referred to here as navigation

    +

    The Cemera as an AI Game +Object

    +

    需要假定环境是封闭的,即有碰撞表面

    + +

    相机的寻路需要考虑环境限制

    +

    Dynamic navigation +techniques

    +

    主要包括以下方法:

      -
    • Updating active cameras
    • -
    • Update camera scripting
    • -
    • Input processing
    • -
    • Pre-think logic
    • -
    • Think ordering
    • -
    • Cinematic camera deferral: 一般来说,cinematic cameras会在一个update -loop的最开始start,而在update loop的最后结束
    • -
    • Post-think logic
    • -
    • Update audio logic
    • -
    • Debug camera
    • -
    • Render setup
    • +
    • Ray casting: +用射线决定是否存在遮挡物体,但是可能会有效率问题,此外,可以增加射线覆盖更大区域。另一种方法是使用ray-casting +hysteresis,即在多个update中累积ray +cast信息,并生成一个可能碰撞的概率图,从而减少每次更新的ray +cast数量。Ray +cast可以用于帮助相机移动。要对物体进行分类以区别哪些需要ray casting +
      For all ray casts
      if ray cast successful
      No influence applied
      if ray cast fails
      Scale influence factor by distance of ray cast collision from target
      Add influence to desired camera position
      End
    • +
    • Volume projection: +该方法把一个volume从相机位置投影到目标位置,用来确定相机的移动是否合法
    • +
    • Sphere/cylinder collisions: +碰撞体的半径应该比近平面距离略大,理论上,碰撞体应该完全包含近平面frustum的四个顶点。在第一人称游戏中,可以不用给相机加碰撞体,因为一般有角色碰撞体即可,但是也要保证近平面被角色碰撞体包围
    • +
    • Dynamic path finding: High-level +solution负责volume之间的整体移动,low-level +solution负责避免碰撞。相机AI不同之处在于移动往往很短,因此更要关注low-level的需求
    • +
    • Dynamic path generation: 不同于path finding,path +generation使用的是游戏中动态生成的信息,所以生成的path可能会动态改变
    • +
    • Visibility and rendering solutions: +这种技术首先决定相机据目标物体的位置,基于一些其他限制,得到可能的相机位置点,对于每个可能的位置,令相机朝向角色,就可以看当前视角是否存在遮挡。决定好位置吼,就可以计算一条移动路径,但是也要注意路径中的遮挡和碰撞
    • +
    • Colliders: +在相机周围添加collider,每个collider都会发射射线,如果有遮挡出现,则移动相机位置 +
      stl::vector<Vec3> influence; 
      // might be useful as a class
      for (int i = 0; i < colliders.size(); ++i)
      {
      influence.push_back(colliders[i].offset * colliders[i].GetWeighting());
      // the weighting depends on line of sight and/or
      // other factors such as the relative collider position
      }
      // will need to get an average or similar
      return GetCentriod(influence);

    -

    Hint manager

    -

    每个CM都有一个与之关联的camera hint manager (CHM)

    -

    Shake manager

    -

    相机震动一般发生在渲染阶段而不是实际移动相机,因为这可能导致相机穿过物体。Shake -transform在局部相机空间中计算应用在渲染之前

    -

    相机震动有三个组件:

    +

    Pre-defined navigation +techniques

    +

    一些常见的pre-defined navigation技术:

      -
    • Sine wave
    • -
    • Amplitude mapping: 把时间映射为每个axis上的振幅
    • -
    • Random noise
    • +
    • Pre-defined paths: 预先做好路径,根据玩家操作在路径上移动和朝向
    • +
    • Path motion behaviors: 有几种方法去限制相机的移动 +
        +
      • Constrained path camera: 相机被固定在path上以避免碰撞
      • +
      • Follow path camera: 相机固定在path上,但移动方式可以有多种方式
      • +
    • +
    • Pre-defined volumes
    • +
    • Attractors/repulsors: +相机会被其他pre-defined的位置、区域、物体所吸引/排斥,吸引力/排斥力的大小取决于相机离该物体的距离和朝向。在合适的时候用attractors/repulsors可以帮助相机避免穿模和在狭窄地区顺利通过,而不需要hand-scripted
    • +
    • Flow fields: Flow +fields是向量的稀疏数列,通常稀疏地分布在2D平面或者3D +Volume内。当相机穿过flow +field时,离相机近的向量会对相机朝向的方向施加一个力。Flow +field常用于一个额外的因子而不是单个因素
    • +
    • Influence maps: 类似flow field,但是通过目标对象离influence +map的相对距离决定而非camera本身,influence +map中的每个点都是相机的可选位置
    • +
    • Potential fields: 基于electrostatic +forces,即对一个物体施加的力的效果取决于发力点和目标物体之间的距离,类似attractors/repulsors
    -

    Game Cameras

    -

    Inherited camera behaviors

    -

    可以用下面的层级去实现相机类:

    +

    Occulision

    +

    Occulusion determination

      -
    • Actor
    • -
    • Camera
    • -
    • Third Person +
    • Ray casting: 通常使用三个部分进行遮挡检测——头部、躯体和脚部
    • +
    • Volume casting
    • +
    • Rendering techniques:
        -
      • Slaved
      • -
      • Observer
      • -
      • Path
      • -
      • Surface
      • -
      • Stationary
      • -
      • Transition
      • -
      • Interpolation
      • -
      • Debug
      • +
      • Flat-shading using identifiers as color values: +待进行遮挡检测的物体使用flat +shading和一个标识颜色,环境物体使用不同的颜色渲染,然后可以通过display +buffer检测物体的遮挡部分
      • +
      • Z-buffer analysis: 直接用depth buffer进行检测
      • +
      • Rendering hardware occulusion queries
    • -
    • First Person
    -

    Component-based camera -behaviors

    -

    不用类继承的方式,另一种方法是使用component-based相机。任何相机的属性都可以拆分为一些可交换的组件,称为behavior,这些behavior能够在运行时组合或交换以产生很多相机变体。一些behavior包括:

    +

    Occulusion prediction +methodologies

    +

    可以用一个predictive相机预测遮挡

    +

    Line of Sight

    +

    Resolving line of sight +problems

    +

    可能有时候相机不能快速移动保持对目标物体的LOS,下面是一些可能的解决方案:

      -
    • Determination of the desired position of the camera
    • -
    • Movement toward the desired position including both collision -determination and response
    • -
    • Orientation determination
    • -
    • Rotation toward a desired orientation
    • -
    • Interpolation
    • -
    • Field of view
    • -
    • The rendering effects to apply to this particular camera's view
    • +
    • Prevention: 手动调整相机位置让它避免出现loss of LOS
    • +
    • Graphical changes: +可以把遮挡物体透明掉或去掉,也可以着重突出角色(如描边、改变颜色、增加indicator)
    • +
    • Finding a desirable position: +可以以角色为中心旋转,或者使用visibility +octree信息,一旦一个位置被发现,则直接把相机relocate到该位置
    • +
    • Teleportation/jump cut: +可以把相机沿着LOS的方向移动,直到没有遮挡(注意要考虑相机的近平面位置与collision +volume)
    • +
    • Retaining control orientation: 相机移动后注意保持相同的retaining +control orientation
    -

    尽管单个component从设计上讲是独立的,但是可能某个特定的component,比如orientation,依赖于其他component的状态,所以需要有特定的component更新顺序

    -

    Cinematic cameras

    -

    通常会把cinematic camera与game -camera分开考虑,有自己独立的viewport

    -

    Debug camera

    -

    只用于render

    -

    Scripting System -Implementation

    -

    Camera script objects

    -

    有时候需要动态改变相机行为,这就是scripting,一些script -objects包括:

    +

    Path generation

    +

    该方法生成一条路径,相机沿着路径移动直到遮挡消失,生成的路径不仅要避免碰撞,并且也要符合美学要求:

      -
    • Camera hints: camera hints通常只是简单的data -repositories,并不要求实际逻辑,除了活跃状态的更新
    • -
    • Trigger volumes
    • -
    • Rendering/lighting effects: -一些渲染效果可以通过给相机发消息实现,或者相机检验游戏状态,或者激活带有特殊效果的相机
    • -
    • Message replay: 引入relay可以使只有一个script object更新
    • -
    • Sequenced event timer: -定义多个游戏事件,它们按照特定的顺序和时间间隔发生
    • -
    • Generic path: 只用一个script object定义path且能够evaluate
    • -
    -

    Ordering of scripting logic

    -

    如果scripting -logic引起任何需要发送的消息,则它们会立即发送给接收者,但是可能此时接收者已经执行了自己的逻辑,所以要等到下个update时才能做出对当前消息的反应,可以把message -cache

    -

    Messaging

    -

    当事件发生时,需要通知其他游戏物体事件的发生,这一般用messaging -system实现。实际的消息只包含了与该事件有关的信息,比如发送消息的物体、消息本身、其他可能触发该消息的物体

    -

    比如当玩家进入trigger volume时,会让对应的script -object发送一个enter消息、是谁发送的消息

    -

    Prioritization

    -

    可用一个整数代表优先级,也可以用属性表示优先级,比如和玩家之间的距离

    -

    Interpolation

    -

    可以用一个专门的interpolation camera管理相机插值

    -

    Performance Considerations

    -

    Amortization

    -

    一般用于缓存几个update内的中间值,但相机移动需要立即执行。在采用amortization之前,需要考虑哪些属性不需要每次update都更新,哪些属性需要立即更新

    -

    Preprocessing

    -

    大多数的相机CPU消耗都用于ray -casting或碰撞检测,预处理,如沿着特定轨道的相机移动,可以减少CPU开销

    -

    Tools Support

    -

    World editor

    -

    大多数常用的属性都以易用的界面实现,比如camera path definition, -camera hint placement,property editing, pre-defined macros of script -objects:

    +
  • Follow character movement: +记录玩家移动的路径并让相机沿着路径移动
  • +
  • Ledge avoidance: +玩家从悬崖边缘下落时相机可能因为无法穿透物体而出现问题,解决方案可能有:
      -
    • Placement/orientation of cameras
    • -
    • Camera property editing
    • -
    • View from the camera while manupulating
    • -
    • Editing in-game then transferring data back
    • -
    • Limit properties shown to those appropriate for behavior
    • -
    • Paths -- automatic waypoint dropping and connections
    • -
    • Volumes: this is the ability to define 3D regions for various -camera-related functionality
    • -
    • Surfaces: defining surfaces for cameras to move on, for example
    • -
    • Links to target objects: identifying target objects for particular -cameras
    • -
    • Control of position/orientation/roll/fov over time (spline -editor)
    • -
    • Evaluation of target object or interpolant derivation over time: -shows where an object will be located over time in case it has an impact -on the camera behavior
    • +
    • Pre-defined scripting solutions for all ledge situations
    • +
    • Use pre-defined solutions for difficult to resolve cases, especially +in confined spaces: 可以用stationary cameras或者spline paths
    • +
    • Teleport the camera to a new position if LOS fails due to the ground +being between the camera and its target: 这可能需要检测剪玩家的高度
    • +
    • Use player movement hysteresis to provide an approximate path taken +by the player character or target object
    • +
    • Interrogate the surrounding geometry to dynamically choose a path +based on the last position at which the player was visible to the +camera: 该位置给出了关于ledge的位置信息用于camera +path,但要保证相机不能离物体太近,也不能让相机垂直朝向
    • +
  • +
  • Vertical column avoidance: Predictive cameras一般能解决该问题
  • +
  • Pre-computed solutions
  • -

    Camera collision mesh

    -

    Camera有自己的collision geometry会更方便,因为可以允许动态改变

    -

    Camera Debugging Techniques

    -

    Interactive debugging

    -

    Interactive debugging包括:

    +

    Avoiding loss of LOS

    +

    如果不能避免loss of LOS,我们可以采取一些方法进行弥补:

      -
    • Internal property interrogation: 直接看相机数据
    • -
    • Separate debugging camera: 维护一个单独的debug -camera,只在开发环境中使用。当使用debug -camera时,支持某些行为很有用,包括:把角色放置在当前相机位置、展示当前相机位置、从当前相机位置投射射线决定环境属性、暂停游戏运行允许操纵debug -camera、捕捉当前渲染buffer并导出
    • -
    • Control of the update rate of the game: -改变游戏的更新率,尤其是单词更新
    • -
    • General camera state: 跟踪一些相机的事项,包括:state information -(active, interpolating, under player control, etc.), script messaging, -changes to active camera hints/game cameras, occlusion state and the -amount of time occluded, fail-safe activation, invalid camera properties -including the validity of the transformation matrix
    • -
    • Visual representation of camera properties: -可视化相机的一些属性,比如用wireframe sphere or -cube展示相机移动,相机朝向、期望看向点、期望朝向
    • -
    • Property hysteresis: 有时候需要查看相机属性的历史记录,比如camera -position (known as breadcrumbs), camera orientation (display orientation -changes as points on the surface of a unit sphere)
    • -
    • Movement constraints: movement path drawing, based on spline curve -evaluations and represented by an approximation of line segments; -movement surface drawing
    • -
    • Line of sight: -沿着forward画线,同时用颜色标记状态,比如红色表示受到阻挡,还可以显示阻挡物体的材质,比如stone, -ceiling等等
    • -
    • Behavior-specific rendering
    • -
    • Script debugging: script statment execution/filtering, debug message -logging, messaging filtering, object state
    • +
    • Instant movement: 有时候很实用
    • +
    • Fade out the obscuring geometry or objects: 会降低玩家沉浸感
    • +
    • Do not render obscuring gheometry or objects at all: +可以用一个timer,只有时间到了才取消渲染
    • +
    • Render a graphical representation regardless of occlusion
    • +
    • Visibility pre-computation
    -

    Data logging

    -

    注意logging对游戏性能的影响,一个优化是cache日志信息直到某个不影响游戏性能的时间点

    -

    在获取log之后,除了直接阅读文本之外,还可以采用可视化的手段,把数据导入游戏复盘相机数据

    -

    Game replaying

    -]]>
    - - 游戏 - 游戏理论 - - - 随笔 - 相机 - 游戏 - 设计 - -
    - - 《游戏设计的236个技巧》笔记 - /2021/08/17/01/07/ - 本文是《游戏设计的236个技巧:游戏机制、关卡设计和镜头窍门》的笔记。

    - -

    前言

    -

    一款优秀的游戏是如何让玩家在某个瞬间感到无比有趣、极度畅快的呢?本书旨在引领读者发现“让游戏更有趣的设计技巧”。 -本书将内容分为“玩家角色”“敌人角色”“关卡设计”“碰撞检测”“镜头”五个部分。

    -

    让3D游戏更有趣的玩家角色技术

    -

    能够吸引2D游戏玩家的3D游戏设计技巧(《超级马里奥兄弟》《超级马里奥3D大陆》)

    +

    Fail-safes

    +

    Fail-safe检测可以分为三类:

      -
    • 马里奥飞奔的感觉让人很舒服。 +
    • Per-update: 在一些情况下必须在每个update进行fail-safe检测
        -
      • 按住十字键开始加速,即使放开十字键也不会马上停住,而是有惯性。
      • -
      • 按B键冲刺的过程中按反方向键,就会出现一个急刹车的动作。
      • +
      • Geometry interpenetration of the camera near plane: +可以用一个足够大的collision volume避免
      • +
      • An invalid (non-normalized) camera transformation matrix: +此时可以orthonormalize或者reconstruct矩阵,或者可以回到上次正确的旋转矩阵
      • +
      • The camera is external to the game world
      • +
      • Excessive rotation required aounrd the world up-axis in one update: +限制旋转角度
      • +
    • +
    • Frequent: 有时候每隔几次update进行一次fail-safe检测 +
        +
      • LOS to garte object extremities
      • +
      • Distance from desired position
    • -
    • 但是塞尔达中林克就没有惯性,让玩家精准停在想停的位置。
    • -
    • 这是因为游戏想带给玩家的游戏体验不同。 +
    • Infrequent: 每隔几秒检测一次
        -
      • 塞尔达的游戏体验是探索与战斗:不需要过多关注奔跑的细节动作。
      • -
      • 超级马里奥的游戏体验是单人挑战障碍赛跑:关注如何更快奔跑、转弯、准确停止并穿越障碍物。将“无法简单停止”作为一种操纵上的风险加入游戏,除了能给游戏带来紧张感之外,还能让玩家在熟悉操作之后获得成就感。
      • +
      • Significant occlusion of player character (3rd person view +only)
      • +
      • Camera distance from target is too far: 会让目标看起来太小
    -

    B键冲刺带来的感官刺激以及风险与回报的趣味性

    +

    一旦触发fail-safe condition,最好是立刻将相机移动到新的safe +position,通常是jump +cut。另一个方法是把相机移动回过去的已知点直到满足条件

    +

    Chapter 9: Motion and +Collision

    +

    Camera Movement Sequence

    +

    我们可以按照下述过程移动相机:

      -
    • 操纵冲刺中的马里奥会给玩家带来畅快感。 +
    • Determine the desired position
    • +
    • Constrain the desired position accordingly
    • +
    • Generate a prospective movement toward the desired position
    • +
    • Test to see if the prosepctive movement will cause collision +(optional)
    • +
    • Resolve the collision (optional)
    • +
    • Generate and validate an alternative movement (optional if first +move fails)
    • +
    • Move the camera to the new position
    • +
    +

    Character motion

    +

    首先看看人物移动会怎样影响第一人称和第三人称相机

      -
    • 通过运动或动作获得感官上的舒畅体验的过程称为感官体验
    • +
    • First person cameras: +注意相机位置通常不是眼睛位置,因此要注意fov和aspect +ratio。有时候玩家跨越世界物体时可能造成traversal +jitter,因此可以增加vertical damping
      // A typical damping scheme for aviuding unwanted noise in the verticasl 
      // motion of the camera is to simply limit the amount allowed per update.
      float const kMaxZMotionPerSecond (0.25f);
      // desired maximum motion
      float zMotion = newPosition.GetZ () - oldPosition.GetZ ();
      float const maxZMotion = kMaxZMotionPerSecond * deltaTime;
      zMotion = Math::Limit (zMotion, maxZMotion);
      newPosition.SetZ (oldPosition.GetZ () + zMotion);

      // The smoothing is relatively harsh: the amount of vertical motion applied
      // is constant and thus will possibly have discontinuities.
      // An alternative is to use a critically damped using spring or PID controller.
      float const zMotion = newPosition.GetZ () - oldPosition.GetZ ();
      verticalSpring.SetLength (AbsF(zMotion));
      // update the spring length
      // Here the target spring length is zero, and need not
      // be set each time. Typically the spring length is unsigned,
      // so that must be dealt with.
      float const newZMotion = verticalSpring.Update (deltaTime);
      if (zMotion > 0.0f)
      newPosition.SetZ (newPosition.GetZ() - newZMotion);
      else
      newPosition.SetZ (newPosition.GetZ() + newZMotion);
    • +
    • Third person cameras: 必须解决相机加速小于角色的问题(motion +lag),和相机减速小于角色的问题(overshooting)
    • +
    +

    Movement methods

    +

    一些移动相机的方法包括:

    +
      +
    • Instantaneous motion: 直接把相机移动到期望的位置,但要注意: +
        +
      • 可能也要改变朝向
      • +
      • 可能需要检测新的位置是否离环境物体太近、没有在物体内
      • +
      • 尽可能保持control reference frame
      • +
      • 尽可能把所有该类型的变换定义为一个函数
      • +
      • 如果出现多普勒效应,则应限制相机移动的最大速度
    • -
    • 有趣秘密还在挑战中蕴含的风险与回报。 +
    • Locked
    • +
    • Proportional controller: 该方法在目标也移动的时候表现不好 +
      Vec3 const deltaPosition = desiredPosition - currentPosition;
      // the distance at which velocity damping is applied
      float const kDampDistance (5.0f);
      float const K = Math::Limit (deltaPosition.Magnitude() / kDampDistance, 1.0f);
      // Limit constant to 0..1 based on distance
      Vec3 const cameraVelocity = deltaPosition.AsNormalized () * K;
      Vec3 const newPosition = currentPosition + cameraVelocity * deltaTime;

      // We can help solve the problem of the camera being left behind by adding a
      // portion of the target object's velocity into the original equation.
      Vec3 targetVelocity = (desiredPosition - previousDesiredPosition) / deltaTime;
      Vec3 = cameraVelocity = (deltaPosition.AsNormalized () * K * deltaTime) + (targetVeclocity * T);

      // To have smooth motion we need to accelerate the camera over time
      // with a limiter to provide some degree of lag in the motion.
      float const acceleration = Math::Limit ( (desiredVelocity - currentVelocity), kMaxAcceleration);
      Vec3 const currentVelocity += acceleration * deltaTime;
      Vec3 const desiredPosition = currentPosition + (currentVelocity * deltaTime);
    • +
    • Physical simulations
    • +
    • Springs: +当目标物体朝相机移动或突然停止时,相机可能出现overshooting问题,我们希望critical +damping,即无论目标物体怎么动,都不会出现overshooting
    • +
    • PID controllers: proportional integral derivative (PID) +controllers,把feedback应用到控制器中减少当前值和期望值的错误量 +
      // The usage of PID controller
      Vec3 const currentDelta = currentPosition - desiredPosition;
      float const currentDistance = currentDelta.Magnitude ();
      float const newDistance = PIDController.Update (desiredDistance, currentDistance, deltaTime);
      Vec3 const currentPosition = desiredPosition + (currentDelta.AsNormalized () * newDistance);
    • +
    • Circular movement: 相机的运动由reference +point到当前位置和期望位置之间的夹角决定,因此需要计算其角速度,有两个方法计算:
        -
      • 马里奥通过加速的移动实现了风险与回报,玩家按住B是一种主动提高风险与回报的行为。
        -
      • +
      • Constant angular velocity: 不管半径多大,以固定的角速度旋转
      • +
      • Constant linear velocity: 保持固定线性速度
        // A typical implementation of moving a camera by constant angular velocity might be:
        Vec3 cameraBearing = Vec3 (currentPosition - referencePoint);
        Vec3 desiredBearing = Vec3 (desiredPosition - referencePoint);
        float const radius = cameraBearing.Magnitude ();
        cameraBearing.Normalize ();
        desiredBearing.Normalize ();
        float const angularMotion = angularVelocity * deltaTime;
        // rads/update
        Quat const q = Quat::ShortestRotationArcClamped (cameraBearing, desiredBearing, angularMotion);
        Vec3 const newBearing = q * cameraBearing;
        // rotate the original bearing
        Vec3 const newPosition = referencePoint + (newBearing * radius);

        // An implementation of constant linear velocity might be:
        Vec3 cameraBearing = Vec3 (currentPosition - referencePoint);
        Vec3 desiredBearing = Vec3 (desiredPosition - referencePoint);
        float const radius = cameraBearing.Magnitude ();
        cameraBearing.Normalize ();
        desiredBearing.Normalize ();
        float const arcLength = linearVelocity * deltaTime;
        float const angularMotion = acosf (arcLength / radius);
        // units/update
        Quat const q = Quat::ShortestRotationArcClamped (cameraBearing, desiredBearing, angularMotion);
        Vec3 const newBearing = q * cameraBearing;
        // rotate the original bearing
        Vec3 const newPosition = referencePoint + (newBearing * radius);
    • -
    • 在超级马里奥兄弟之前,动作类游戏的跳跃一直都是跳跃到固定高度,但是超马采用了按键时长与跳跃高度相关的机制。
    • -
    • 在跳跃时按方向键可以控制马里奥在空间左右移动。
    • -
    • 在跳跃过程中碰到墙壁等障碍物不会下落,这虽然有悖物理,但是给了玩家更舒适的游戏体验。
    • +
    • Interpolation: +考虑在两个相机的速度之间插值,为了避免discontinuity,要让加速度匹配而不仅仅是速度
    -

    勾起玩家跳跃冲动的互动式玩法

    +

    Smoothing and damping +techniques

    +

    Damping方法通常要用ease +functions,把一个输入值(0到1之间)映射到相同区间,只不过是非线性映射且开始和结尾的导数为零

      -
    • 宫本茂说:“我认为互动的乐趣之一在于:一个人对自己的某种想法付诸实践之后,能够获得相应的反馈”。
    • -
    • 《超马》为跳跃这一动作准备了大量的反馈。 +
    • Motion damping: 可以通过添加vertical damping减少颠簸感
    • +
    • Motion filters: 用一个低通滤波过滤噪音
      Vec3 const movementDelta = newPosition - currentPosition;
      if (movementDelta.Magnitude () > kMovementThreshold) {
      currentPosition = newPosition;
      } else {
      // ignoring small changes may cause the camera never to move at all!
      }
      +下面是一种改进的方法(使用finite impulese response and infinite impulse +response filters):
      Vec3 const movementDelta = newPosition - currentPosition;
      if (!close_enough (movementDelta, Vec3::Zero ())) {
      float distance = movementDelta.Magnitude ();
      distance = mMovementFilter.Update (distance);
      currentPosition += movementDelta.AsNormazlied () * distance;
      }
    • +
    +

    Motion constraints

    +

    一般有下述几种相机约束:

      -
    • 操作复杂度<动作数<反馈数
    • -
    • 即用较少的动作获得较多的反馈。
    • -
    -
  • 那么《超马》凭什么让人能够不自由自主地想要按下跳跃键呢? +
  • Vertical motion constraint: +用于避免不想要的图形效果如穿模,比如避免水面穿帮可以用下面的代码: +

    float const kMinimumZDistance (0.1f);
    float const zDistance (currentPosition.GetZ () - waterPlane.GetZ ());
    if (fabs (zDistance) < kMinimumZDistance)
    {
    currentPosition.SetZ (waterPlane.GetZ () + Math::Sign (zDistance) * kMinimumZDistance);
    }
    +显然,上述代码的问题在于相机在进入水面后会出现不连续移动,但这无法避免,但可以用特效过渡

  • +
  • Render geometry proximity: 用collision volume解决

  • +
  • Distance constraints: +可以让相机和玩家保持固定的距离或最小/最大距离,但是可能会有问题 +

    Vec3 const unconstrainedOffset = currentPosition - desiredPosition;
    float const currentDistance = inverseDirection.Magnitude ();
    float const newDistance = Math::Clamp (kMinDistance, currentDistance, kMaxDistance);
    Vec3 const newOffset = unconstrainedOffset.AsNormalized () * newDistance;
    currentPosition = desiredPosition + newOffset;

  • +
  • Planaer constraints

    CPlane const plane (normal, constant);
    Vec3 cameraPosition (GetCurrentPosition ());
    float const planeDistance = Vec3::Dot (plane.GetNormal (), cameraPosition) - plane.GetConstant ();
    if (planeDistance < kMinDistance)
    {
    cameraPosition += plane.GetNormal () * (planeDistance - kMinDistance);
    }

  • +
  • Surface constraints

  • +
  • Volume constraints

  • + +

    Player camera control

    +

    一个原则: > The player should be not REQUIRED to manipulate the +camera simply to play the game, unless explicitly dictated by the game +design.

    +

    If camera-relative, the sheer act of moving the camera changes the +control reference frame and "pulls the rug out from under the player's +feet". Sadly, some games seem to treat this behavior as acceptable. It +is not. Changing the control reference frame without player knowledge is +counter to good game play practice.

      -
    • 存在让玩家不由自主想要去实践地机制,称其为“游戏的钓饵”。比如玩家用小马里奥顶砖块,获得的反应是砖块向上被拱起这一动画。 -这一动画会在玩家心中放下一个钓饵,让玩家觉得砖块里藏着什么。
    • -
    • 游戏里一定要有引诱玩家付诸实践的钓饵
    • +
    • Maunipulation of the camera position: +有时候允许玩家直接操纵相机,分为2D和3D相机 +
        +
      • 2D相机: 2D游戏通常会限制相机的移动以防看到游戏外的场景
      • +
      • 3D相机: 在不同相机之间切换可以使用插值或jump cut
    • +
    • Observer cameras: 提供观察者相机,即可以随意自由移动的相机
    • +
    • Camera motion validation: 一些游戏不允许操纵相机位置
    • +
    • Positional control constraints: +Character-relative相机通常保持在玩家身后的一定区域内,一般不会让相机面朝角色。相机移动要尽可能平滑和慢以避免player +disorientation
    -

    从《2D马里奥》到《3D马里奥》

    +

    Camera position control +schemes

    +

    主要有两种方法决定相机位置:

      -
    • 《3D马里奥》出乎意料地取消了移动动作的惯性,可能是因为3D视角的距离感相比2D要更难以掌握,加入惯性会使操作难度上升。
    • -
    • 《3D马里奥》中镜头角度局限在了“侧面”“上方”“倾斜”三种固定模式中,而不是像一般3D游戏将视角决定权交给玩家。同时,滑垫也被限制在了16个方向以内,让镜头角度与移动角度保持一致,从而帮助玩家找回2D游戏的感觉,同时也避免了因斜向移动导致误判跳跃距离。
    • -
    • 同时,在《超级马里奥3D大陆》中还首次加入了在跳跃过程中改变方向的机制。
    • -
    • 加入了翻滚、远跳、翻滚跳等动作,吸引高端玩家重复挑战关卡。
    • +
    • Character-relative: +类似unity的free-look相机,但需要加一些限制,比如相机的移动范围是一个cylindrical而不是整个球面、相机在人物朝向的身后一定范围内。一些相机允许半自动的移动,比如移动向一个危险区域时
    • +
    • World-relative: +此时相机的朝向和玩家朝向没关系,比如RTS、运动、3D平台游戏中的相机
    -

    小结

    +

    Manipulation of camera +orientation

      -
    • 要给玩家带来“感官刺激”“风险与回报”“动作与反应”和“连环钓饵”。
    • -
    • 游戏体验的真实性并不仅取决于贴图和声效等外观因素,通过游戏内部机制表现出的“互动的乐趣”也有巨大影响。
    • +
    • First person cameras
    • +
    • Third person cameras:
    -

    让游戏更具临场感的玩家角色动作设计技巧(战神三)

    -

    不需控制镜头的移动操作机制

    +

    Automated camera +positioning and orientation

    +

    除了cinematic +sequences之外,也有一些情况会需要相机自动改变位置和朝向:

      -
    • 战神是TPS游戏,角色的移动方向与遥感倾斜方向一致。也会自动选取最佳角度。
    • -
    • 采用自动镜头的3D动作游戏需要很好地处理镜头移动。 +
    • In response to player controller usage: 玩家自由选择视角
    • +
    • In response to game play requirements: +在三人称游戏中,有时候我们想保证某个物体一直在视野内,有时候,会通过相机给玩家辅助(比如在跳跃时增加pitch)
    • +
    +

    Debug camera control

    +

    该相机需要在不影响游戏的情况完全由用户操作,同时,如果能在降低游戏运行速度的情况能以正常速度操作debugging +camera,则会非常有利于debug

    +

    Camera Collisions

    +

    The importance of camera +collisions

    +

    Camera collision可能引起的问题:

      -
    • 可以为每个场景设置不同风格的自动镜头,同时让玩家体验到2D游戏简单而又直观的操作感,以及3D游戏身临其境般的操作感。
    • -
    • 通过自动镜头,战神三创造出了电影般的临场感与魄力。
    • -
    +
  • The collision prevents the camera from moving to its desired +position
  • +
  • Discontinuous motion may be caused by collisions
  • +
  • Occulusion of the target object
  • +
  • Camera near plane intersects with geometry
  • -

    实现快节奏战斗的玩家移动动作机制

    +

    Collision determination

      -
    • 要实现快节奏战斗,玩家必须能够灵活转身、迅速静止,因此战神三的静止具有即时性,同时旋转左摇杆可以实现角色的原地旋转。
    • -
    • 《怪猎4》玩家的转身半径要大于战神三,原地回头的时间也略长。 +
    • Object collisions
    • +
    • Environmental collisions: 有两种数据——render mesh和collision +mesh
    • +
    • Collision primitives: 通常来说,相机会被表示为球体用于碰撞检测
    • +
    +

    Collision geometry design

    +

    下面是一些设计场景的tips:

      -
    • 怪猎4想创造出“狩猎”的感觉。
    • -
    -
  • 每个游戏都有一套能让玩家觉得舒服的“速度”(转身速度、移动速度、拔刀速度、收刀速度、硬直速度)“节奏”(具体的动作设计、动作之间的连贯性)和“触感”(动作的打击感)。根据这三点找出合适的玩家角色动作机制,是现代游戏开发的重点。
  • +
  • Avoid tightly confined spaces
  • +
  • Allow sufficient space for the camera
  • +
  • Steep steps
  • +
  • Ledges/overhangs
  • +
  • Low ceilings
  • +
  • Doorways: +第一,玩家可能移动太快以至于门在玩家和相机之间关闭,此时可以让门保持开启直到相机穿过;第二,玩家可能沿着门缝移动并导致相机无法跟随玩家,此时可以让相机沿着门缝的轴移动;第三,玩家可能站在门缝里,此时要避免相机因玩家旋转而与物体相交
  • +
  • Use the simplest collision mesh possible
  • +
  • Separate collision mesh for the camera
  • +
  • Chamfer all corners: 为此可以让相机沿着collision表面滑动
  • -

    不带来烦躁感的地图切换机制

    +

    Collision resolution

    +

    当我们检测到发生碰撞时,就需要决定相机怎么办,可以有以下几种策略:

      -
    • 战神三在切换地图之后玩家仍然继承之前的移动方向。只要玩家不放开左摇杆方向,就会继续沿着切换前的方向行进。
    • -
    • 3D动作游戏的核心是:让玩家角色的动作准确反映玩家意图
    • +
    • Do not move: 此时如果人物移动就可能会有问题
    • +
    • Partial move: 尝试向期望方向移动,如果还是有碰撞则不动
    • +
    • Alternative movement: 改变相机的移动方向
    • +
    • Jump cut: 重新计算desired position并立即切换过去
    -

    让人不由得手指发力的玩家角色动作机制

    +

    Disabling collision +detection

    +

    有时候我们不需要相机的碰撞检测。比如第一人称和第三人称视角的切换、path-based +cameras。总之取决于期望的相机效果

    +

    Avoiding camera collisions

    +

    可以动态生成一条相机跟随的路径,或者统一采用半透明的方式。在三人称游戏中,当玩家在墙角或无法移动的地区时,相机可能和角色碰撞,此时可以切换到第一人称中,或者旋转相机调整相机位置

    +
      +
    • Future position of player
    • +
    • Predicting potential collisions
    • +
    • Collision prediction
    • +
    • Object repulsion
    • +
    • Movement toward the camera
    • +
    +

    Chapter 10: Camera +Mathematics

    +

    摄像机的一些数学:

    +
      +
    • Camera position +
        +
      • Offset within the local space of an object
      • +
      • Angular offsets relative to game objects
      • +
      • Valid position determination
      • +
      • Spline curves
      • +
    • +
    • Camera orientation +
        +
      • Representation
      • +
      • Look-at
      • +
      • Shortest rotation arc
      • +
      • Roll removal
      • +
      • Twist reduction
      • +
      • Determining angles between desired orientations
      • +
    • +
    • Camera motion +
        +
      • Proximity
      • +
      • Damping functions
      • +
      • Springs
      • +
      • Interpolation
      • +
    • +
    • Rendering +
        +
      • Camera space/world space/screen space conversions
      • +
      • FOV conversion
      • +
      • Frustum construction
      • +
      • Perspective projection
      • +
      • Orthographic projection
      • +
      • Isometric projection
      • +
      • Axonometric projection
      • +
    • +
    • General camera math
        -
      • 为烘托角色服务的动作需要由互动动作实现。 +
      • Digital filters
      • +
      • Spline curves
      • +
      • Interpolation
      • +
    • +
    • Camera math problems and fixes
        -
      • 比如开宝箱这个动作需要玩家长按R1,主角运足全身力量掀开宝箱的动画贯彻玩家按键始终,引得玩家下意识加大按键力道。
      • -
      • 开门同理。
      • -
      • 这种称为互动性演出
      • +
      • Floating-point accuracy and precision
      • +
      • Epsilon usage
      • +
      • Base conversion
    -

    小结

    +

    Common Mathematical +Techniques

    +

    Look-at

    +

    此时通过一个旋转矩阵或四元数把当前的相机朝向变换到新的朝向

    +
    Mat4 const Mat4::LookAt (Vec3 const& source, Vec3 const& dest, Vec3 const& worldUpVector)
    {
    Vec3 viewDirection = Vece (dest - source).AsNormalized ();
    float dot = Vec3::Dot (viewDirection, worldUpVector);
    Vec3 unnormalizedUp = worldUpVector - (dot * vewDirection); // 相机的unnormalized Up axis
    Vec3 up = unnormalizedUp.AsNormalized (); // 相机的up axis
    Vec3 right = Vec3::Cross (up, viewDirection);

    // matrix ordering depends on row/column major
    return Mat4 (
    right[X], viewDirection[X], up[X], source[X],
    right[Y], viewDirection[Y], up[Y], source[Y],
    right[Z], viewDirection[Z], up[Z], source[Z]
    )
    }
    +

    注意到上面的变换矩阵是把世界空间中的点变换到相机在(0,0)点的位置

    +

    Roll removal

    +

    为了移除roll,我们需要重新计算view transform的right +axis同时保持当前的forward vector,然后可以通过叉乘决定up vector

    +

    Twist reduction

    +

    在三人称相机中,当物体在相机上方或者下方的时候,可能导致相机快速旋转。一个方法是检测相机绕着forward +vector旋转的速度,但是只有在forward vector几乎在和world up +axis平行的时候

    +

    World space to screen +space conversion

    +

    取决于projection算法

      -
    • 战神三在角色动作上的设计创造玩家与玩家角色融为一体的真实游戏体验。
    • +
    • Orthographic projection
      uint32 const ScreenX = ObjectX - ViewportX; // assumes top left corner is (0,0)
      uint32 const ScreenY = ViewPortY - ObjectY; // depends on the direction of +Y
      +如果viewport是显示设备的子集,或者viewport涉及缩放,则必须考虑这些因素: +
      uint32 const ScreenX = Viewport.ScreenX + (ObjectX - Viewport.WorldX);
      uint32 const ScreenY = Viewport.ScreenY + (Viewport.WorldY - ObjectY);
    • +
    • Isometric projection
      uint32 const ScreenX = (ObjectX - Viewport.WorldX) * 2;
      uint32 const ScreenY = Viewport.WorldY - ObjectY;
    • +
    • Perspective projection
    -

    让割草游戏更有趣的攻击动作设计技巧(战神三)

    -

    让攻击准确命中目标敌人的机制

    +

    Screen space to +camera/world space conversion

    +

    通常来说,神都会被设置为相机的近平面,或者一个特定的距离相机的距离

    +
    Vec3 const ConvertToWorldSpace (Mat4 const& cameraTransform, Vec3 const& screenPosition)
    {
    Vec3 const viewSpace = GetProjectionMatrix ().Inverted ().MultiplyOneOverW (screenSpacePosition);
    Vec3 const worldSpace = cameraTransform * viewSpace;
    return worldSpace;
    }
    +

    FOV conversion

    +

    考虑把HFOV转化为VFOV,假设我们知道了近平面距离和aspect +ratio,与HFOV

    +

    我们定义变量alphabeta: +

    alpha = HFOV / 2
    beta = VFOV / 2
    tan (alpha) = (viewport width / 2) / near plane distance
    tan (beta) = (viewport height / 2) / near plane distance

    +

    从而有:

    +
    tan (alpha) * near plane distance = viewport width / 2
    Near plane distance = (viewport width / 2) / tan (alpha)
    tan (beta) * near plane distance = viewport height / 2
    Near plane distance = (viewport height / 2) / tan (beta)
    +

    进而有:

    +
    (viewport width / 2) * tan (beta) = (viewport height / 2) * tan (alpha)
    tan (beta) = [(viewport height / 2) / (viewport width / 2)] * tan (alpha)
    tan (beta) = aspect ratio * tan (alpha)
    VFOV = 2 * arctan (tan (HFOV / 2) * aspect ratio)
    +

    所以,只要知道了aspect ratio和其中一个FOV,就可以求出另一个FOV

    +

    Quaternions

    +

    四元数的好处在于可以很方便地插值,且没有Gimbal Lock问题。

    +

    Bump and Ease Functions

    +

    Bump和ease +functions都用来保证开始和结束的平滑,ease函数分为ease-in和ease-out函数,ease-in函数也叫damping函数。下面讨论几个简单的函数来生成S型曲线

    +

    Exponentials

    +

    使用简单的三次函数:

    float exp = 3t^2 - 2t^3;

    +

    Proportional

    +

    Proportional ease +function使用当前值和期望值之间的差值作为输入,常用一个damping range: +

    float deltaValue = desiredValue - currentValue;
    if (fabs(deltaValue) < kRange>)
    {
    float factor = Math::Limit (deltaValue / kRange, 1.f);
    currentValue += kSpeed * factor * deltaTime;
    }

    +

    Proportional damping +function实现简单且高效,但是可能不如其他方法平滑。另一种方法是根据经过的时间计算factor: +

    float const dampingFactor (1.0f - elapsedTime / totalTime);
    interpolant += (destinationValue - interpolant) * dampingFactor * deltaTime;
    return interpolant;

    +

    Spherical linear +interpolation

    +

    球面线性插值

    +

    Transcendentals

    +

    用三角函数:

    +
    angle = (pi * time factor) - (pi / 2);  // in radians, -pi/2 ... pi/2
    time factor = (sinf(angle) + 1) / 2
    +

    Springs

    +

    当实现相机移动的时候我们常常碰到相机overshoot的问题,这是因为目标在不断移动,阻止相机足够快地改变方向、速度以避免被目标对象自身抢占位置,但同时我们也不想相机过快移动,这就导致了矛盾

    +

    此时我们可以使用弹簧方案,基于下述的方程:

    +

    +

    是弹性常量,一个正数表示弹簧的弹性,是弹簧伸缩度,这样我们可以写出下面的伪代码: +

    Vec3 deltaPosition = desiredPosition - currentPosition;
    Vec3 movementDirection = deltaPosition.AsNormalized ();
    float extension = deltaPosition.Magnitude ();
    float force = -kSpringConstant * extension; // for a unit mass, this is acceleration, F = ma
    Vec3 newVelocity = currentVelocity + (movementDirection * force);
    new Position = currentPosition + (currentVelocity * deltaTime);

    +

    弹簧方案的一个问题是设置弹簧的弹性常量,并且会出现震荡现象,这可以用两种思路解决:

      -
    • 战神三并没有锁定操作,实际上,游戏一致在进行锁定和解除锁定,只是玩家没有察觉到而已。 +
    • 使用damped spring,控制施加的力使得弹簧接近但永远不会超过目标
    • +
    • 使用feedback controller
    • +
    +

    Digital Filters

    +

    在控制器输入或相机移动中,我们想要移除小的震荡,此时可用滤波器

    +

    Low pass

    +

    低通滤波让低频信号通过,而过滤高频信号

    +

    High pass

    +

    与低通滤波相反

    +

    Band

    +

    移除某个频率范围之外的信号

    +

    Finite impulse response

    +

    有限脉冲响应滤波在计算当前值的时候结合之前的输入值,因为易于实现,所以与IIR滤波相比更倾向于FIR滤波

    +

    History +buffer中的每一项都有一个对应的滤波系数,对应了该项目的影响有多大,即改变对输入值的响应度

    +
    static float const firCoefficients[] = 
    {
    // these values greatly influence the filter
    // response and may be adjusted accordingly
    0.f, 0.f, 0.f, 1.f, 2.f, 3.f, 4.f
    };
    float CFIRFilter::Initialize (void)
    {
    // setup the coefficients for desired response
    for (int i = 0; i < mHistoryBuffer.size(); ++i)
    mCoefficients[i] = firCoefficients[i];
    }
    float CFIRFilter::Update (float const input)
    {
    // copy the entries in the history buffer up by one
    // position (i.e. lower entries are more recent)
    for (int i = mHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mHistoryBuffer[i + 1] = mHistoryBuffer[i];
    }
    mHistoryBuffer[0] = input;
    float fir(0);
    // now accumulate the values from the history
    // buffer multiplied by the coefficients
    for (int i = 0; i > mHistoryBuffer.size(); ++i)
    {
    fir += mCoefficients[i] * mHistoryBuffer[i];
    }
    return fir;
    }
    +

    Infinite impulse response

    +

    不同于FIR,IIR也加入了之前的输出值

    +
    float CIIRFilter::Initialize (void)
    {
    mInputCoefficients[0] = 0.5f;
    mInputCoefficients[1] = 0.3f;
    mOutputCoefficients[0] = 0.5f;
    mOutputCoefficients[1] = 0.3f;
    }
    float CIIRFilter::Update (float const input)
    {
    for (int i = mInputHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mInputHistoryBuffer[i + 1] = mInputHistoryBuffer[i];
    }
    mInputHistoryBuffer[0] = input;
    float const result =
    mInputCoefficients[0] * mInputHistoryBuffer[0] +
    mInputCoefficients[1] * mInputHistoryBuffer[1] +
    mOutputCoefficients[0] * mOutputHistoryBuffer[0] +
    mOutputCoefficients[1] * mOutputHistoryBuffer[1];
    for (int i = mOutputHistoryBuffer.size() - 2; i >= 0; --i)
    {
    // not efficient! Can use circular buffer
    mOutputHistoryBuffer[i + 1] = mOutputHistoryBuffer[i];
    }
    mOutputHistoryBuffer[0] = result;
    return result;
    }
    +

    Spline Curves

    +

    Spline由一系列控制点和对应的tangent +vector控制,每个segment通常由两个控制点组成,每个控制点有1~2个切线向量。相邻两个segment之间共享的控制点叫做joint

    +

    Camera spline usage

    +

    相机Spline的通常用法是定义一条路径,再用一个evaluation函数将时间映射到路径上的位置。二维spline通常用来表示物体的属性,三维spline通常用来表示空间位置

    +

    Cubic polynomials

    +

    三次多项式可以表示为:

    +

    +

    有时候需要给定解出

    +

    Spline types

    +

    下面介绍几种不同的spline types:

      -
    • 玩家倾斜左摇杆时,玩家角色会锁定移动方向上最近的敌人。
    • -
    • 锁定过程中,角色会一直面朝敌人。
    • -
    • 可以通过左摇杆锁定其他敌人,如果超出一定距离,锁定会解除。
    • -
    • 消灭敌人后,锁定会自动解除,如果还有其他敌人,会继续自动锁定。
    • +
    • Linear: 一系列直线,用下面的代码计算位置
      Vec3 const Linear (Vec3 const & a, Vec3 const & b, float const time)
      {
      return a + ((1.f - time) * Vec3 (b - a));
      }
    • +
    • Piecewise Hermite: +Hermite曲线要求两个控制前和两个切线,每个控制点一条切线。我们也可以为每个控制点引入两条切线——in-tangent和out-tangent,很多动画系统用这种方法 +
      const Vec3 Hermit (Vec3 const & a, Vec3 const & b, Vec3 const & startTangent, Vec3 const & endTangent, float const time)
      {
      if (u <= 0.0f)
      return a;
      else if (u >= 1.0f)
      return b;

      float const t2 (time * time);
      float const t3 (t2 * time);

      // Calculate basis functions
      float const a0 = (t3 * 2.0f) - (3 * t2) + 1.0f;
      float const a1 = (-2.0f * t3) + (3.0f * t2);
      float const b0 = t3 - (2.0f * t2) + u;
      float const b1 = t3 - t2;

      // Use cubic basis functions with points and tangents
      Vec3 const result ((a0 * a) + (a1 * b) + (b0 * startTangent) + (b1 * endTangent));
      return result;
      }
    • +
    • Catmull-Rom: Catmull-Rom spline +C1连续、局部控制,但是并不位于控制点形成的convex hull中 +
      Vec3 const CatmullRom (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
      {
      if (time <= 0.0f)
      return b;
      if (time >= 1.0f)
      return c;

      float const t2 = time * time;
      float const t3 = t2 * time;

      Vec3 const result = (a * (-0.5f * t3 + t2 - 0.5f * time) +
      b * (1.5f * t3 - 2.5f * t2 + 1.0f) +
      c * (1.5f * t3 + 2.0f * t2 + 0.5f * time) +
      d * (0.5f * t3 - 0.5f * t2));
      return result;
      }
    • +
    • Rounded Catmull-Rom: 能够决定两个控制点中间曲线的速度 +
      Vec3 const RoundedCatmullRom (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
      {
      if (time <= 0.0f)
      return b;
      if (time >= 1.0f)
      return c;

      // find velocities at b and c
      Vec3 const cb = c - b;
      if (!cb.IsNormalizable ())
      return b;
      Vec3 ab = a - b;
      if (!ab.IsNormalizable ())
      ab = Vec3 (0, 1, 0);
      Vec3 bVelocity = cb.AsNormalized () - ab.AsNormalized ();
      if (bVelocity.IsNormalizable ())
      bVelocity.Normalize ();
      else
      bVelocity.Vec3 (0, 1, 0);

      Vec3 dc = d - c;
      if (!dc.IsNormalizable ())
      dc = Vec3 (0, 1, 0);
      Vec3 bc = -cb;
      Vec3 cVelocity = dc.AsNormalized () - bc.AsNormalized ();
      if (cVelocity.IsNormalizable ())
      cVelocity.Normalize ();
      else
      bVelocity = Vec3 (0, 1, 0);

      float const cbDistance = cb.Magnitude ();
      return CatmullRom (b, c, bVelocity * cbDistance, cVelocity * cbDistance, time);

      }
    • +
    • Kochanek-Bartels splines: KB spline是CR +spline的扩展,因为引入了其他的参数来控制spline的曲率。有三个参数: +
        +
      • Bias: +控制每个tangent的方向,-1使曲线提早buckle,+1使曲线buckle到末尾
      • +
      • Tension: 控制每个tangent vector的长度, ++1导致更紧的曲线,-1导致更圆的曲线,如果大于+1则产生环
      • +
      • Continuity: +控制tangents之间的角度,-1使曲线向里buckel,+1使边角朝向相反方向 +
        Vec3 const KBSpline (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time,
        float const tension, float const continuity, float const bias)
        {
        // tension, continuity ans bias defined per segment
        Vec3 const ab = Vec3 (b - a).AsNormalized ();
        Vec3 const cd = Vec3 (d - c).AsNormalized ();
        Vec3 const inTangent = ((1.f - tension) * (1.f - continuity) * (1.f + bias)) * 0.5f * ab +
        ((1.f - tension) * (1.f + continuity) * (1.f - bias)) * 0.5f * cd;
        Vec3 const outTangent = ((1.f - tension) * (1.f + continuity) * (1.f + bias)) * 0.5f * ab +
        ((1.f - tension) * (1.f - continuity) * (1.f - bias)) * 0.5f * cd;
        return PiecewiseHermite (b, c, inTangent, outTangent, time);
        }
        如果所有参数都为零,则退化为CR spline
    • +
    • Bézier: 二次贝塞尔曲线方程为
    -

    让连击畅快淋漓的机制

    +

    +
    Vec3 const QuadraticBezier (Vec3 const & a, Vec3 const & b, Vec3 const & c, float const time)
    {
    float const oneMinusTime (1.f - time);
    Vec3 const bezier = (a * oneMinusTime * oneMinusTime) + (b * 2.f * time * oneMinusTime) + (c * time * time);
    return bezier;
    }
    +

    三次贝塞尔曲线方程为:

    +

    +
    Vec3 const CubicBezier (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
    {
    float const oneMinusTime (1.f - time);
    Vec3 const bezier = (a * oneMinusTime * oneMinusTime * oneMinusTime) + (b * 3.f * time * oneMinusTime * oneMinusTime) + (c * 3.f * time * time * oneMinusTime) + (d * time * time * time);
    return bezier;
    }
      -
    • 普通攻击以灵活为主,一般会在第三击转换为大范围横向攻击。
    • -
    • 重攻击出招慢,以纵向为主。
    • -
    • 开始以普通攻击震慑周围敌人,再以重击给单个敌人予以重创。
    • -
    • 战神三在设计攻击种类和连击招式时,为每一个攻击动作都分配了固定的用途,玩家能够享受制定战术的乐趣。
    • -
    • 每一个招式都是由攻击动画 -、攻击力攻击方向追踪性能等要素组合而成。 +
    • Uniform cubic B-spline: B spline是一种泛化的Bezier +spline,一个好处是它有local +control,即:每个控制点只会影响整个曲线的一小部分,它使用下述的blending +function
    • +
    +

    +
    Vec3 const BSpline (Vec3 const & a, Vec3 const & b, Vec3 const & c, Vec3 const & d, float const time)
    {
    float const t2 (time * time);
    float const t3 (t2 * time);

    Vec3 const result ((a * (-t3 + (3 * t2) + (-3 * time) + 1)) +
    (b * ((3 * t3) + (-6 * t2) + 4)) +
    (c * ((-3 * t3) + (3 * t2) + (3 * time) + 1)) +
    (d * t3));
    return (result / 6.0f);
    }
      -
    • 所谓“追踪性能”,就是指玩家角色发动攻击招式时,根据已锁定的敌人所在的位置自动进行追踪的功能。
    • -
    • 普通攻击威力小但追踪性能高,重击威力大但追踪性能低。
    • -
    • 如果一款游戏的连击系统能让玩家觉得畅快淋漓,那么其对追踪的调整一定十分到位。
    • -
    +
  • Non-uniform rational B-spline
  • -

    菜鸟也能轻松上手的畅快的浮空连击机制

    +

    Continuity

    +

    C0是位置连续,C1是速度连续,C2是加速度连续。所以C1或更高的连续会在视觉上效果更好,C2或更高的连续会有更平滑的移动

    +

    Spline definitions

    +

    为了充分地定义spline curve,我们需要几个机制:

      -
    • 割草类游戏都将空中连击的下落设置得比较慢。
    • -
    • 在战神三中,玩家只需长按键就可发动挑空攻击。
    • -
    • 此外,在玩家随挑空攻击后插入了短暂的慢放,相当于一个信号,帮助玩家准确把握发动空中连击的时机。
    • +
    • 一个能够在游戏世界放置控制点的编辑器,甚至可以在游戏运行的时候生成
    • +
    • Spline evaluation type
    • +
    • 跨国spline的总的时间或一个映射函数
    • +
    • 一个可视化展示spline curve的界面,随参数改变动态变化
    -

    用简单操作发动复杂连击的机制

    +

    Spline evaluation

    +

    可以预先计算整个spline的长度,然后用一个线性长度作为spline上的位置。如果再搭配速度damping则会产生比较平滑的效果

    +

    Control point generation

      -
    • 两个攻击键,短按与长按。
    • -
    • 如果连击第一招需要判断长按还是短按,需要在“长按”判定结束之前,所有受影响的连击都应用同一个攻击动画。
    • -
    • 战神三中的连击只有第一招需要判定按键长短,连击过程不需要。《鬼泣》和《猎天使魔女》则支持连击过程长按。
    • -
    • 战神三还可以在连击过程中切换武器。
    • +
    • 单个控制点:总是返回这个点
    • +
    • 两个控制点:要提供额外的tangent信息,否则无法确定曲线
    • +
    • 三个控制点:可以外推第四个控制点
    -

    让玩家角色动作更细腻的设计技巧(《塞尔达传说:天空之剑》)

    +

    Parameterized arc length

    +

    积分法

    +

    Total spline length

    +

    总长度是各个spline弧长度的和,往往采用数值方法近似曲线长度

    +

    Closest position

    +

    计算spline上离某个位置最近的点往往很有用,但问题是可能有多个解,这时候就需要对这些解做个排序,比如考虑相机的前一个位置或者LOS,或者相机的角速度

    +
    Determine the nornal at each end of the segment and by using similar triangles
    Determine the length on the segment
    If length within 0..1 then
    Convert linear 0..1 into a parametric value
    With parametric value 0..1 find the position within the arc using the usual spline evaluation
    Check position against source position for LOS and other factors
    If OK, determine the physical distance between two points and compare to current "best"
    Else
    Not within segment, so proceed to next segment
    +

    Spline editing

    +

    开发能够编辑spline的工具

    +

    Interpolation

    +

    线性插值在一对数据点中生成新数据,piecewise插值则是由几个连续的数据点去生成。相机朝向往往用非线性插值

    +

    Camera Property +Interpolation

    +

    Position interpolation

    +

    位置插值应该是基于当前的位置和目标位置,用这个距离相比于原来的距离

    +
    Vec3 interpFactor = CMath::Clamp (0.0f, interpTimer/maxInterpTime, 1.0f);
    Vec3 currentDeltaPosition = desiredPosition - GetTranslation ();
    Vec3 newPosition = GetTranslation () + (currentDeltaPostion * interpFactor);
    // when interpTimer reaches maxInterpTime, the camera reaches the destination
    +

    Orientation interpolation

    +

    朝向插值通常不涉及roll,即使需要考虑roll,也会单独处理

    +

    由于小的朝向改变会引起巨大的视觉变化,所以一定要确保朝向插值的平滑。线性插值并不保证对象一定在视野里。如果朝向改变很大,要限制角速度,或者使用jump +cut。朝向插值也要保证路径最短

      -
    • 将玩家动作与角色为动作融为一体。 ### -支撑海量解密内容的玩家角色移动动作
    • -
    • 按下Z键,镜头会自动调整至林克面朝的方向。
    • -
    • 在悬崖边行走不会跌落。
    • +
    • Orientation interpolation by angle: NLerp, Slerp, +Squad。基于角度的插值的一个缺点是:相机可能会看得远离目标物体,取决于变化的速度。这可能发生在原相机和目标相机都看向同一点,此时在插值的过程中相机会偏离目标点
    • +
    • Orientation interpolation by target position: +可以用基于目标位置的插值保证目标始终在视野内,但此时朝向速度的变化非常重要
    • +
    • Orientation interpolation characteristics: +当前朝向和期望朝向之间的角度要随着插值越来越小,可以通过两个方法实现:(1)反向计算从目标朝向到当前朝向;(2)保证当前插值角小于等于前一步插值角,如果因为目标朝向改变导致角度增大,则使用前一个插值相机的朝向,一旦插值相机朝向接近目标朝向,则把朝向锁定
    • +
    • Roll interpolation: +用最短方向插值roll,但一般来说roll只用于短时间的相机效果
    -

    让玩家下意识选择合适动作的Z注视机制

    +

    FOV interpolation

    +

    如果没有其他渲染效果,那么只改变FOV就显得很突兀,快速的FOV改变会让玩家不适,这时候一个jump +cut反而更好。zoom in也是通过FOV实现的

    +
    float targetFOV = GetTargetFOV ();
    float deltaFOV = mFOV - targetFOV;
    float newFOV = targetFOV + (deltaFOV * deltaTime);
    float newDeltaFOV = newFOV - targetFOV;
    if (absF(newDeltaFOV) < absF(deltaFOV))
    {
    mFOV = newFOV;
    }
    +

    Viewport Interpolation

    +

    改变显示设备的大小,通常用于cinematic sequences和regular game +play之间的转换。比如从2.35:1到1.33:1

    +

    Player Control Interpolation

    +

    First person cameras

    +

    control interpolation通常用于从一个第一人称control reference +frame变化到一个fixed control reference frame比如object orbiting

    +

    Third person cameras

    +

    在很多情况下,我们要保证如果相机出现巨大移动,则玩家控制不会马上改变

    +

    一个问题是如何处理玩家移动比相机移动快的问题,而且玩家绕着相机垂直轴的任何移动会导致相机绕着该轴的快速转动

    +

    Interpolation Choices

    +

    首先要问现有的数据是kay value还是control +values;第二,要考虑插值方法的效率和效果;第三,要考虑插值结果的平滑性;最后,也要考虑不同插值方法需要的数据量

    +

    Linear interpolation

    +

    线性插值只需要三个参数:source value, destination +value和interpolation method

    +

    Destination value往往会随时间改变,我们就需要考虑到destination +value的变化率,当插值的变化率不匹配目标值的变化率时,不连续就会发生

    +

    插值方法需要考虑是否要用固定的时间完成插值。不考虑变化率会在开始和结束时产生不连续现象

    +

    Piecewise interpolation

    +

    通常有多于两个数据点进行插值,一种策略是把它们视为独立的interpolation +segments:

    bool foundTime (false);
    for (int i = 0; i < valueTimes.Size() - 1; ++i)
    {
    if (valueTimes[i+1] > time)
    {
    float timeDelta = valueTimes[i+1] - valueTimes[i];
    timeFactor = (time - valueTimes[i]);
    segment = i;
    source = values[i];
    destination = values[i+1];
    foundTime = true;
    break;
    }
    }
    if (foundTime)
    return Interpolate (source, destination, timeFactor);

    +

    此外,也可以在每对数据点中假设均匀时间区间,但会有比较大的问题

    +

    Methods of Interpolation

    +

    Linear time interpolation

    +
    Interpolated value = source + ((destination - source) * t)
    +

    Parametric functions

    +

    比如ease函数

    +

    Spherical linear +interpolation

    +

    用于角的插值

    +

    Potential Interpolation +Problems

    +

    Aesthetic problems

    +

    一些视觉上的问题包括:

      -
    • 采用“普通移动”“Z注视移动”“Z注视锁定敌人移动”三种移动模式。
    • +
    • Geometry interpenetration
    • +
    • Discontinuities
    • +
    • Interpolation noise
    • +
    • Target object framing
    • +
    • Large orientation changes
    • +
    • Large interpolation distances
    -

    能单独当游戏玩的移动动作——奋力冲刺

    +

    Mathematical problems

    +

    数学上的问题一般是由浮点误差导致的:

      -
    • 冲刺消耗耐力值,设置了只有奋力冲刺才能通过的斜坡。
    • -
    • 翻滚冲刺、沿墙冲刺等等。
    • +
    • Co-linear (or exactly opposed) orientation
    • +
    • Coincident positions: 0为除数
    • +
    • Floating-point inaccuracy
    -

    没有跳跃键却可以体验真实跳跃的机制

    +

    Interruption of +Interpolation

    +

    有可能在一个插值过程中触发了另一个插值,这时候可以从当前值开始直接开始新的插值

    +

    Transitions

    +

    Position during transitions

    +

    Orientation during +transitions

    +

    有两种方法在transition的过程中控制插值相机的朝向,如果source和des相机没有reorienting,那么简单的线性插值或slerp可以处理得很好,但如果有一个或者两个都reorient就会有问题。一个方法是计算从目标值到当前值之间得剩余量(比如角度),如果新的插值大于当前值,则忽略

    +

    Camera Math Problems

    +

    Floating-point precision

    +

    相机系统一般用单精度浮点数,所以不能表示很大的数

    +

    Epsilon usage

    +

    要根据用途改变epsilon的值

    +

    Compiler differences

    +

    编译器也有不同

    +

    Hardware FPU differences

    +

    Vector normalization

    +

    Matrix concatenation +floating-point drift

    +

    在应用旋转矩阵后,正交化该矩阵以保证浮点精确度

    +

    Periodic Camera Mathematical +Fixes

    +

    最好在渲染之前检查当前的相机变换是不是正确的,包括:

      -
    • 自动触发跳跃,但并没有失去跳跃的乐趣。玩家依然需要掌控距离,需要助跑等等。
    • -
    • 自动跳跃可以避免空气墙。
    • +
    • 变换矩阵的每个元素不包含异常值,比如NaN
    • +
    • 变换矩阵的每个向量都是单位向量
    • +
    • Up-vector和游戏内的Up-vector一致
    • +
    • 必要时移除roll
    • +
    • 必要时限制相机快速的reorientation
    -

    头脑与身体一起享受的剑战动作设计技巧(《塞尔达传说:天空之剑》)

    -

    能帅气挥剑的机制

    +

    如果矩阵不能被修复,则使用上一帧的矩阵;记得多正交化矩阵

    +

    Chapter 11: Implementation

    +

    Game Engine Architecture

    +

    Game update loop

    +

    大多数相机逻辑都要在其他逻辑之后执行,典型的执行顺序是:

      -
    • 要将遥控器当成真剑来操作,带来了前所未有的战斗动作享受。
    • -
    • Wii将人的动作符号化,让普通玩家也能享受到乐趣。
    • +
    • Process input
    • +
    • Process pre-logic on game objects
    • +
    • Process main logic (AI, etc.) on game objects
    • +
    • Move game objects and resolve collisions
    • +
    • Process all camera logic
    • +
    • Process post-camera logic for objects (such as dependencies on +camera position, orientation, etc.)
    • +
    • Process all viewport logic
    • +
    • Render scene for each viewport
    -

    攻击与体力的机制

    +

    Game system managers

    +

    一些管理系统包括:

      -
    • 游戏系统设定的连击结束时攻击即结束,最后一招往往动作较大、破绽较多。
    • -
    • 为玩家设置体力参数,归零时攻击结束。
    • -
    • 玩家没有体力或者没有挥动Wii时,攻击结束。
    • -
    • 一般动作游戏是A与B结合,而天空之剑是B与C组合。
    • +
    • Camera manager: +跟踪所有的相机,传递输入、确保每个都得到正确更新
    • +
    • Object manager: 管理游戏中的每个对象,保证逻辑处理顺序正确
    • +
    • Message manager
    • +
    • Audio manager: 通过它控制音效的变化
    • +
    • Input manager: 将输入信息传递给所有需要的游戏对象
    -

    让玩家痛快反击的盾击机制

    +

    Delta time

    +

    现在游戏通常将逻辑层与渲染层分开,这有利于CPU处理和GPU渲染,游戏逻辑可能以固定的速率更新,也可能以动态的速度更新,从而,相邻两次更新的时间间隔不一定一致,经过的时间就称为delta +time

    +

    Input processing

    +

    很多相机需要把输入值传递给它们,这通常由相机管理器实

    +

    Camera System Architecture

    +

    Viewport manager

    +

    主要工作是包含所有在场景内需要渲染的东西,处理控制器输入、渲染、masking、buffer管理、aspect +ratio等等,能够被其他游戏系统用于访问关于当前活跃相机的相机系统。数据结构是:

    +
    class CViewportManager
    {
    public:
    typedef int32 TViewportHandle;
    TViewportHandle const CreatViewport (EViewportType);
    void DeleteViewport (TViewportHandle const viewport);
    void Update (float const deltaTime);
    void ProcessInput (CInput const & input);
    CViewport const & GetViewport (TViewportHandle const handle) const;
    }
    +

    在定义了显示媒介之后,我们需要描述投影,因此VM需要:

      -
    • 盾击会让敌人暂时失去平衡,创造反击的机会。
    • -
    • 盾击是通过“玩家盾击动作”与“敌人攻击动作”中设置的“盾击成功判定帧”进行判定的。如果这两个动作中的“盾击成功帧”重合,则判定盾击成功。
    • -
    • 盾牌加入耐久。
    • +
    • Accessors for viewports
    • +
    • Render all viewports
    • +
    • Input handler
    • +
    • Create a new viewport
    • +
    • Activate or deactivate an existing viewport
    • +
    • Delete an existing viewport
    • +
    • Transition (morph) between two viewports
    • +
    • Change viewport properties
    • +
    • Assign an active camera for a viewport
    -

    实现剑战动作的机制

    +

    每个Viewport都控制了所有需要渲染的信息,比如相机、控制器输入、surface +locatio and +size、渲染模式等等,甚至还包含了用于多种输出设备渲染需要的信息

    +

    VM还处理不同viewport之间的过渡,通常用于玩家在菜单界面、暂停时,或者画中画。Transition可能包括:

      -
    • 当面对多个敌人时,只集中精力与一名敌人对战,基本上是一对一。
    • -
    • 通过Z注视机制实现剑战。受到Z注视的敌人会先积极攻击。
    • +
    • Cut
    • +
    • Wipe
    • +
    • Cross fade
    • +
    • Morphing
    -

    剑战动作与割草游戏的区别

    +

    Render manager

    +

    从当前的viewports和cameras获取数据并渲染,此外会移除frustum外的的物体,决定渲染顺序,应用后处理

    +

    Camera manager

    +

    CM的主要责任是扮演所有相机的控制器,生成相机新实例、处理转场、插值、优先级、控制器输入、replay +modes和时间控制

    +

    CM有Update函数,处理步骤是:

      -
    • 战斗中的跳跃不同。塞尔达采用了自动跳跃,不会出现跳跃闪避攻击的情况。战神则可以通过跳跃躲避敌人。
    • -
    • 割草游戏有大量的AOE,塞尔达大多为单体攻击。
    • +
    • 决定哪个相机需要开始或结束
    • +
    • 更新活动相机的逻辑(插值相机最后)
    • +
    • 传递控制器输入
    • +
    • 更新audio系统的位置和速度信息
    • +
    • 设置渲染环境
    -

    完美演绎英雄的玩家角色动作设计技巧(《蝙蝠侠:阿甘之城》)

    -

    能像蝙蝠一样在三维空间自如穿梭的机制

    +

    Camera update loop

    +

    相机一般是下面的更新逻辑:

      -
    • 滑翔与抓钩就是“平面+高度”的移动手段,还有其他作用。
    • -
    • 在大楼之间移动时,玩家只需要按住X操作左摇杆,就能根据间隔和高度进行跳跃或自动切换至滑翔状态。不会因为跳跃失误而遭失败。
    • -
    • 抓钩也实现了精准操作和粗略操作。
    • +
    • Updating active cameras
    • +
    • Update camera scripting
    • +
    • Input processing
    • +
    • Pre-think logic
    • +
    • Think ordering
    • +
    • Cinematic camera deferral: 一般来说,cinematic cameras会在一个update +loop的最开始start,而在update loop的最后结束
    • +
    • Post-think logic
    • +
    • Update audio logic
    • +
    • Debug camera
    • +
    • Render setup
    -

    通过简单操作实现高自由度的玩家角色动作的机制

    +

    Hint manager

    +

    每个CM都有一个与之关联的camera hint manager (CHM)

    +

    Shake manager

    +

    相机震动一般发生在渲染阶段而不是实际移动相机,因为这可能导致相机穿过物体。Shake +transform在局部相机空间中计算应用在渲染之前

    +

    相机震动有三个组件:

      -
    • 玩家角色的移动动作就有很多种,移动动作并不由玩家的手柄决定,而是由玩家触碰的物体决定。(类似刺客信条)
    • -
    • 蝙蝠侠将移动动作特有的难点——时机把握从基本移动操作中剔除,是一款专注功能可供性的动作游戏。
    • +
    • Sine wave
    • +
    • Amplitude mapping: 把时间映射为每个axis上的振幅
    • +
    • Random noise
    -

    演绎一名不会轻易死亡的英雄

    +

    Game Cameras

    +

    Inherited camera behaviors

    +

    可以用下面的层级去实现相机类:

      -
    • 在奔跑中碰到墙壁,移动动画会终止。
    • -
    • 在滑翔时碰到墙壁,蝙蝠侠也不会落下去,而是抓住墙壁。
    • -
    -

    让玩家化身为英雄的设计技巧(《蝙蝠侠:阿甘之城》)

    -

    让战术自由度更高的机制

    +
  • Actor
  • +
  • Camera
  • +
  • Third Person
      -
    • 潜行动作游戏在游戏中的流程分为“侦察”“制定战术”“捕食战术”“格斗战术”四个阶段。
    • -
    • 蝙蝠侠在捕食战斗阶段能削减敌人多少就成了战斗取胜的关键。
    • -
    • 蝙蝠侠提供了50多种不同的动作,具有极高的自由度。
    • +
    • Slaved
    • +
    • Observer
    • +
    • Path
    • +
    • Surface
    • +
    • Stationary
    • +
    • Transition
    • +
    • Interpolation
    • +
    • Debug
    • +
  • +
  • First Person
  • -

    让人忍不住要尝试的工具机制

    +

    Component-based camera +behaviors

    +

    不用类继承的方式,另一种方法是使用component-based相机。任何相机的属性都可以拆分为一些可交换的组件,称为behavior,这些behavior能够在运行时组合或交换以产生很多相机变体。一些behavior包括:

      -
    • 蝙蝠侠中有很多工具,或者道具,一共有20多种。这些工具不会击溃敌人,只会造成负面效果,保证了工具不会打破战斗平衡性。
    • -
    • 工具和战斗动作可以组合,使得产生“有趣的反应”和“新动作”
    • +
    • Determination of the desired position of the camera
    • +
    • Movement toward the desired position including both collision +determination and response
    • +
    • Orientation determination
    • +
    • Rotation toward a desired orientation
    • +
    • Interpolation
    • +
    • Field of view
    • +
    • The rendering effects to apply to this particular camera's view
    -

    让玩家完美演绎蝙蝠侠的捕食者动作的机制

    +

    尽管单个component从设计上讲是独立的,但是可能某个特定的component,比如orientation,依赖于其他component的状态,所以需要有特定的component更新顺序

    +

    Cinematic cameras

    +

    通常会把cinematic camera与game +camera分开考虑,有自己独立的viewport

    +

    Debug camera

    +

    只用于render

    +

    Scripting System +Implementation

    +

    Camera script objects

    +

    有时候需要动态改变相机行为,这就是scripting,一些script +objects包括:

      -
    • “无声压制”和“转角隐蔽压制”可以无声无息地击溃敌人,所以玩家能够立刻开始下一步动作而不被敌人发现。
    • -
    • “粉碎重击”和“边缘压制”能产生很大的声响。
    • -
    • 利用有利位置,可以将敌人吊起来剥夺其行动力的“倒吊压制”。
    • +
    • Camera hints: camera hints通常只是简单的data +repositories,并不要求实际逻辑,除了活跃状态的更新
    • +
    • Trigger volumes
    • +
    • Rendering/lighting effects: +一些渲染效果可以通过给相机发消息实现,或者相机检验游戏状态,或者激活带有特殊效果的相机
    • +
    • Message replay: 引入relay可以使只有一个script object更新
    • +
    • Sequenced event timer: +定义多个游戏事件,它们按照特定的顺序和时间间隔发生
    • +
    • Generic path: 只用一个script object定义path且能够evaluate
    -

    改变动作游戏定式的自由流程格斗机制

    +

    Ordering of scripting logic

    +

    如果scripting +logic引起任何需要发送的消息,则它们会立即发送给接收者,但是可能此时接收者已经执行了自己的逻辑,所以要等到下个update时才能做出对当前消息的反应,可以把message +cache

    +

    Messaging

    +

    当事件发生时,需要通知其他游戏物体事件的发生,这一般用messaging +system实现。实际的消息只包含了与该事件有关的信息,比如发送消息的物体、消息本身、其他可能触发该消息的物体

    +

    比如当玩家进入trigger volume时,会让对应的script +object发送一个enter消息、是谁发送的消息

    +

    Prioritization

    +

    可用一个整数代表优先级,也可以用属性表示优先级,比如和玩家之间的距离

    +

    Interpolation

    +

    可以用一个专门的interpolation camera管理相机插值

    +

    Performance Considerations

    +

    Amortization

    +

    一般用于缓存几个update内的中间值,但相机移动需要立即执行。在采用amortization之前,需要考虑哪些属性不需要每次update都更新,哪些属性需要立即更新

    +

    Preprocessing

    +

    大多数的相机CPU消耗都用于ray +casting或碰撞检测,预处理,如沿着特定轨道的相机移动,可以减少CPU开销

    +

    Tools Support

    +

    World editor

    +

    大多数常用的属性都以易用的界面实现,比如camera path definition, +camera hint placement,property editing, pre-defined macros of script +objects:

      -
    • 在一般的动作游戏种,玩家需要根据周围敌人的情况选择固定的攻击动作或连击,然后输入指令来发动。 +
    • Placement/orientation of cameras
    • +
    • Camera property editing
    • +
    • View from the camera while manupulating
    • +
    • Editing in-game then transferring data back
    • +
    • Limit properties shown to those appropriate for behavior
    • +
    • Paths -- automatic waypoint dropping and connections
    • +
    • Volumes: this is the ability to define 3D regions for various +camera-related functionality
    • +
    • Surfaces: defining surfaces for cameras to move on, for example
    • +
    • Links to target objects: identifying target objects for particular +cameras
    • +
    • Control of position/orientation/roll/fov over time (spline +editor)
    • +
    • Evaluation of target object or interpolant derivation over time: +shows where an object will be located over time in case it has an impact +on the camera behavior
    • +
    +

    Camera collision mesh

    +

    Camera有自己的collision geometry会更方便,因为可以允许动态改变

    +

    Camera Debugging Techniques

    +

    Interactive debugging

    +

    Interactive debugging包括:

      -
    • 要先考虑与敌人的距离、招式的速度、招式的追踪性能、招式的属性。
    • -
    -
  • 自由格斗,玩家输入的不是攻击招式,而是适合周围状况的攻击动作。
  • -
  • 一个按键对应多个攻击招式以及敌人反应。
  • -
  • 蝙蝠侠仍然是一款根据环境自动选择动作的游戏。
  • +
  • Internal property interrogation: 直接看相机数据
  • +
  • Separate debugging camera: 维护一个单独的debug +camera,只在开发环境中使用。当使用debug +camera时,支持某些行为很有用,包括:把角色放置在当前相机位置、展示当前相机位置、从当前相机位置投射射线决定环境属性、暂停游戏运行允许操纵debug +camera、捕捉当前渲染buffer并导出
  • +
  • Control of the update rate of the game: +改变游戏的更新率,尤其是单词更新
  • +
  • General camera state: 跟踪一些相机的事项,包括:state information +(active, interpolating, under player control, etc.), script messaging, +changes to active camera hints/game cameras, occlusion state and the +amount of time occluded, fail-safe activation, invalid camera properties +including the validity of the transformation matrix
  • +
  • Visual representation of camera properties: +可视化相机的一些属性,比如用wireframe sphere or +cube展示相机移动,相机朝向、期望看向点、期望朝向
  • +
  • Property hysteresis: 有时候需要查看相机属性的历史记录,比如camera +position (known as breadcrumbs), camera orientation (display orientation +changes as points on the surface of a unit sphere)
  • +
  • Movement constraints: movement path drawing, based on spline curve +evaluations and represented by an approximation of line segments; +movement surface drawing
  • +
  • Line of sight: +沿着forward画线,同时用颜色标记状态,比如红色表示受到阻挡,还可以显示阻挡物体的材质,比如stone, +ceiling等等
  • +
  • Behavior-specific rendering
  • +
  • Script debugging: script statment execution/filtering, debug message +logging, messaging filtering, object state
  • -

    还原机器人动画的玩家角色动作设计技巧(《终极地带:引导亡灵之神》)

    +

    Data logging

    +

    注意logging对游戏性能的影响,一个优化是cache日志信息直到某个不影响游戏性能的时间点

    +

    在获取log之后,除了直接阅读文本之外,还可以采用可视化的手段,把数据导入游戏复盘相机数据

    +

    Game replaying

    ]]>
    游戏 - 游戏理论 随笔 + 相机 游戏 - -
    - - 一道有趣的概率题 - /2022/10/20/23/59/ - 独立同分布,令,求。换句话说,我们要求最先使得若干个独立同分布于的随机变量之和大于的期望。

    - -

    问题定义

    -

    这个问题的定义已经定义在上面了,这里再复述一遍:

    -

    独立同分布,令,求

    -

    引入函数

    -

    乍一看这个题似乎无从下手,但是我们可以发现这里这个条件似乎可以换成任意一个,这启发我们用一个函数去表示我们要求的式子,然后通过求解一个“递推式”(实际上是一个微分方程)解出这个函数,进而得到某个具体点的值。

    -

    从这个思路出发,我们不妨定义,进而令。显然,我们的目标就是求

    -

    求解方程

    -

    那么,怎么求出呢?注意题目中的条件独立同分布,因此我们可以把里面的拆出来,变成:

    -

    -

    为了简便起见,我们不妨限定。此时考虑两种情况:

    -

    如果,那么就等于,所以这等价

    -

    如果,那么就相当于后面的,再由独立同分布知道这就是,但这是以为条件的,所以实际上还要对求个积分,也就是:

    -

    -

    把上面两个加起来,就有:

    -

    -

    进而得到微分方程及其初始值:

    -

    -

    很容易求解得到,所以我们就能得到最终的答案

    -]]>
    - - 数学 - 概率论 - - - 数学 - 随笔 - 概率论 - -
    - - 你为什么不笑了 - /2022/10/29/00/35/ - 你为什么不笑了?

    - -

    奶奶,你为什么不笑了?
    -还记得,一年前的你,
    -走在春风里,
    -向着山顶慢慢登去。
    -你摘下路边的一朵浅梅,
    -捧在手心。
    -阳光洒下,
    -手中的梅花,也映衬出了你脸上的笑容。

    -

    你的儿子女儿挽着你的手,
    -一步一步向山顶走去,
    -你看见一棵古树,
    -你说这是拍照好去处。
    -女儿扶着你,
    -你扶着树,
    -阳光洒下,
    -树上的绿叶,也甘愿陪衬你脸上的笑容。

    -

    来到山顶,
    -黄花遍地,
    -你开心得像个孩子,
    -伸出双手,
    -比了两个大大的耶,
    -那时的你,真的好美。

    -

    奶奶,从什么时候开始,你不笑了?
    -从拔掉爷爷呼吸机的那个夜晚开始,
    -你似乎一直睡得不好。
    -你还能梦见吗,
    -另一张空荡荡的床上,
    -曾经那个老人的模样?
    -从前他是那样健康,
    -如今却骨瘦嶙峋,
    -让时间风干了皮肤,
    -侵蚀了思想。
    -你还能看见吗,
    -那个明媚下午的阳光,
    -案前那个老人伏着阅读着,报刊也泛黄。

    -

    奶奶,你为什么不笑了?
    -近日来,
    -你总是抱怨,
    -胸口有恙。
    -你变得啰嗦、唠叨,
    -总是对小事斤斤计量。
    -我好想你能多笑笑,
    -放下生活的疲劳,
    -忘却心中的郁结,
    -再回忆,
    -我们一起出游时,
    -小鸟的啼叫,
    -鲜花的绽放,
    -阳光的照耀,
    -我们手拉着手,
    -在田园中,聊些家常,传来欢笑。

    -

    奶奶,时间真的很残酷,
    -也许当我走时,
    -这个世界便再无你的回响,
    -人生人寂,人来人往,
    -至我去时,此情已了,略无痕迹。

    -

    奶奶,再让我看看你的笑,
    -那个阳光中,
    -像孩子般最天真的笑容。

    -]]>
    - - 随笔 - - - 随笔 - 生活 + 设计
    @@ -6330,102 +6330,43 @@ id="还原机器人动画的玩家角色动作设计技巧终极地带引导亡

    其中一个基向量我们已经找到了,就是,而另一个我们可以通过的叉乘实现,得到的向量与垂直,且在平面内。且注意到: 并且有: 进而我们能导出旋转后的垂直分量 -最后,我们得到旋转后的向量

    -

    -

    搞定!

    -

    矩阵形式

    -

    我们知道向量的叉乘可以表示为: 注意到,矩阵有如下的性质: -所以我们可以把旋转公式写成下述形式: -其中。上面的等式需要注意到

    -

    所以,使用Rodrigues'旋转公式,只需要首先令,然后再计算,就能得到旋转后的向量为

    -

    方法二:坐标轴对齐

    -

    既然直接绕着任意轴旋转比较困难,那为啥不先进行整个空间的旋转,把旋转轴旋转为坐标轴,这样就能把向量绕任意轴旋转转化为向量绕标准坐标轴旋转。这就是我们非常熟悉的问题了。

    -

    假定我们考虑的是三维空间的旋转(对更高维的情况容易推论),即标准坐标系为。我们有旋转轴和待旋转向量

    -

    首先,我们构建一个坐标系,该坐标系的一个轴就是,我们利用叉乘实现:

    -

    -

    现在,我们要把坐标轴分别旋转到坐标轴的位置,这可以用下面的旋转矩阵实现:

    -

    -

    很容易验证:,从而就有,这就验证了是正交的。

    -

    现在,原来的向量就变成了,原来绕旋转(也就是绕旋转)就变成了绕旋转,而我们知道绕轴旋转的旋转矩阵是: 因此,旋转后的向量就是。现在,只需要把旋转后的向量再旋转回原来的位置就好了,我们只需要再乘以的逆即可。由于是正交的,所以有。把上面的结果合起来,就能得到最终的结果是: -

    -

    检验

    -

    现在我们用代码来检验一下上述三种方法是否能得到同样的结果,以及它们的运算效率如何。比较的方法包括: -1. 向量分解-向量形式 2. 向量分解-矩阵形式 3. 对标轴对齐

    -

    程序在虚拟机上运行,RAM为4G,硬盘20G,处理器为2个Intel Core i5-10400F -CPU @ 2.90GHz。

    -

    代码如下:

    #include<iostream>
    #include<eigen3/Eigen/Eigen>
    #include<string>
    #include<opencv2/opencv.hpp>
    #include<chrono>

    using namespace std;

    const double PI = 3.1415926;

    // rotation using vector decomposation - the vector form
    void DecomposeVector(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Vector3f rotatedVector = cos(rotationAngle) * p
    + (1 - cos(rotationAngle)) * (n.dot(p)) * n
    + sin(rotationAngle) * (n.cross(p));

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: vector decomposition - the vector form. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // rotation using vector decomposation - the matrix form
    void DecomposeMatrix(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Matrix3f N = Eigen::Matrix3f::Identity();
    N << 0, -n(2), n(1),
    n(2), 0, -n(0),
    -n(1), n(0), 0;
    Eigen::Matrix3f R = Eigen::Matrix3f::Identity() + sin(rotationAngle) * N + (1 - cos(rotationAngle)) * N * N;
    Eigen::Vector3f rotatedVector = R * p;

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: vector decomposition - the matrix form. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // rotation using axis coordination
    void AxisCoordination(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Vector3f crossed = n.cross(p);
    Eigen::Vector3f u = n;
    Eigen::Vector3f v = crossed / crossed.norm();
    Eigen::Vector3f w = n.cross(v);
    Eigen::Matrix3f Q = Eigen::Matrix3f::Identity(), T = Eigen::Matrix3f::Identity();
    Q.row(0) = u;
    Q.row(1) = v;
    Q.row(2) = w;
    T << 1, 0, 0,
    0, cos(rotationAngle), -sin(rotationAngle),
    0, sin(rotationAngle), cos(rotationAngle);
    Eigen::Vector3f rotatedVector = Q.transpose() * T * Q * p;

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: axis coordination. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // main
    int main()
    {
    float angle = 60;
    Eigen::Vector3f p(1.0, 2.0, 3.0);
    Eigen::Vector3f n(2.0, 8.6, -3.1);
    n.normalize();

    // execute functions
    cout << "The vector p is (" << p(0) << "," << p(1) << "," << p(2) << "). "
    << "The rotation axis n is (" << n(0) << "," << n(1) << "," << n(2) << "). " << endl;
    DecomposeVector(n, p, angle);
    DecomposeMatrix(n, p, angle);
    AxisCoordination(n, p, angle);

    return 0;
    }
    输出是:
    The vector p is (1,2,3). The rotation axis n is (0.213724,0.919011,-0.331271). 
    Method: vector decomposition - the vector form. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.016354
    Method: vector decomposition - the matrix form. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.016642
    Method: axis coordination. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.021062
    再多试几组: -
    The vector p is (1,-654.1,12.88). The rotation axis n is (0.995044,0.0136933,-0.0984901). 
    Method: vector decomposition - the vector form. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.015503
    Method: vector decomposition - the matrix form. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.015297
    Method: axis coordination. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.021547

    -
    The vector p is (32.5,45.1,-2.2). The rotation axis n is (0.57735,0.57735,0.57735). 
    Method: vector decomposition - the vector form. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.015558
    Method: vector decomposition - the matrix form. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.015215
    Method: axis coordination. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.020841
    -
    The vector p is (666,0,0). The rotation axis n is (0,0,1). 
    Method: vector decomposition - the vector form. The rotated vector p' is (1.78454e-05,666,0). The time used is 0.01517
    Method: vector decomposition - the matrix form. The rotated vector p' is (0,666,0). The time used is 0.015402
    Method: axis coordination. The rotated vector p' is (1.78454e-05,666,0). The time used is 0.020898
    -

    最后一组出现了精度问题,所以代码中还应该加入判断

    if(abs(value - round(value)) < epsilon)
    value = round(value);

    -

    从上面的例子来看,向量分解-向量形式向量分解-矩阵形式运行效率是一致的,而坐标轴对齐的效率较低,这主要是由计算三次矩阵乘法导致的。

    -]]> - - 数学 - 图形学 - - - 数学 - 随笔 - 计算机 - -
    - - 根据角色体型和俯角自动确定FreeLook镜头参数 - /2022/06/21/23/40/ - 最近项目涉及到一个需求:如何自动确定镜头的参数(在Unity中即为轨道的高度和半径)。这个问题可以形式化为:给定角色身高、看向点偏移、俯角和人物在画面中的占高比,自动计算出相机到看向点的距离、相机中轨的半径和相机中轨的高度。本文推导出该计算公式,实现镜头参数自动化处理。

    - -

    角色高度、看向点偏移、俯角与相机参数

    -

    我们的第一步目标是:给定角色高度、看向点偏移和俯角,能够自动计算出相机距离、相机轨道的半径和高度。在此之前,首先需要明确几个概念:

    -
      -
    • 角色高度:角色模型在游戏中的实际高度,因为我们目前关心的角色在屏幕中的占高比,暂不考虑角色宽度;
    • -
    • 看向点偏移:从角色Root结点的Y轴偏移量,如果等于角色高度则位于角色头顶位置,等于零则位于Root位置;
    • -
    • 俯角:相机看向角色看向点时与XZ平面的夹角;
    • -
    • 相机距离:相机到看向点的距离;
    • -
    • 相机轨道高度:相机从角色Root点开始的Y轴高度;
    • -
    • 相机轨道半径:相机轨道在当前高度上的半径。
    • -
    -

    这几个参数的关系可以用下图来表示:

    -
    -几个参数之间的关系。X:角色高度,Y:看向点偏移,A:俯角,D:相机距离,H:相机高度,R:相机半径。 - -
    -

    我们不难得出下面的关系:

    -

    -

    但这是对的情况,当时,我们有:

    -

    -

    但是由于我们在计算俯角时,以下方向为正方向,当时,俯角实际上是负值,所以也不需要加负号。于是,最终的公式仍然是统一的。

    -

    人物高度、人物占高比、FOV与相机距离

    -

    上述公式的一个前提是需要知道可自定,),而我们实际上关注的是人物在整个画面中的占高比,而相机屏幕与FOV有关,所以为了计算,实际上就是要考虑通过FOV和人物高度、人物占高比计算出相机距离。

    -

    现在我们假定人物占高比固定为,可以根据需求自行推导。

    -
    -通过FOV和人物高度计算出相机距离。我们固定了1/3的人物占高比。 - -
    -

    如上图所示,相机投影的上半部分角度为FOV的一半,在这里我们假设FOV为,那么一半就是。设相机距离为,当角色高度的一半,即占屏幕上半的时,我们不难得到下述公式:

    -

    -

    但是上述公式没有考虑俯角,这就会导致一定的误差。下面我们分两种情况讨论,如下图所示:

    -
    -修正后的相机距离用俯角、FOV和人物高度计算得出。我们固定了1/3的人物占高比。 - -
    -

    角色半高在相机屏幕空间中的投影为。所以我们能够得到下述修正公式:

    -

    -

    该公式对两种情况都是适用的。

    -

    总结

    -

    总的来说,只要给定角色身高、看向点偏移(一般来说设置为或者)和俯角,在固定人物在画面中占高比和FOV为的前提下,就可以根据下述公式计算相机到看向点距离、相机中轨半径和相机中轨高度

    -

    -

    如果你需要改变人物在画面的占高比,或者相机的FOV,只需要按照上面的推导方法修改公式即可。

    -

    当然,你也可以继续精细化上述公式,如考虑实际的看向点偏移,但一般来说差别不是很大,在此不再赘述。

    +最后,我们得到旋转后的向量

    +

    +

    搞定!

    +

    矩阵形式

    +

    我们知道向量的叉乘可以表示为: 注意到,矩阵有如下的性质: +所以我们可以把旋转公式写成下述形式: +其中。上面的等式需要注意到

    +

    所以,使用Rodrigues'旋转公式,只需要首先令,然后再计算,就能得到旋转后的向量为

    +

    方法二:坐标轴对齐

    +

    既然直接绕着任意轴旋转比较困难,那为啥不先进行整个空间的旋转,把旋转轴旋转为坐标轴,这样就能把向量绕任意轴旋转转化为向量绕标准坐标轴旋转。这就是我们非常熟悉的问题了。

    +

    假定我们考虑的是三维空间的旋转(对更高维的情况容易推论),即标准坐标系为。我们有旋转轴和待旋转向量

    +

    首先,我们构建一个坐标系,该坐标系的一个轴就是,我们利用叉乘实现:

    +

    +

    现在,我们要把坐标轴分别旋转到坐标轴的位置,这可以用下面的旋转矩阵实现:

    +

    +

    很容易验证:,从而就有,这就验证了是正交的。

    +

    现在,原来的向量就变成了,原来绕旋转(也就是绕旋转)就变成了绕旋转,而我们知道绕轴旋转的旋转矩阵是: 因此,旋转后的向量就是。现在,只需要把旋转后的向量再旋转回原来的位置就好了,我们只需要再乘以的逆即可。由于是正交的,所以有。把上面的结果合起来,就能得到最终的结果是: +

    +

    检验

    +

    现在我们用代码来检验一下上述三种方法是否能得到同样的结果,以及它们的运算效率如何。比较的方法包括: +1. 向量分解-向量形式 2. 向量分解-矩阵形式 3. 对标轴对齐

    +

    程序在虚拟机上运行,RAM为4G,硬盘20G,处理器为2个Intel Core i5-10400F +CPU @ 2.90GHz。

    +

    代码如下:

    #include<iostream>
    #include<eigen3/Eigen/Eigen>
    #include<string>
    #include<opencv2/opencv.hpp>
    #include<chrono>

    using namespace std;

    const double PI = 3.1415926;

    // rotation using vector decomposation - the vector form
    void DecomposeVector(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Vector3f rotatedVector = cos(rotationAngle) * p
    + (1 - cos(rotationAngle)) * (n.dot(p)) * n
    + sin(rotationAngle) * (n.cross(p));

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: vector decomposition - the vector form. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // rotation using vector decomposation - the matrix form
    void DecomposeMatrix(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Matrix3f N = Eigen::Matrix3f::Identity();
    N << 0, -n(2), n(1),
    n(2), 0, -n(0),
    -n(1), n(0), 0;
    Eigen::Matrix3f R = Eigen::Matrix3f::Identity() + sin(rotationAngle) * N + (1 - cos(rotationAngle)) * N * N;
    Eigen::Vector3f rotatedVector = R * p;

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: vector decomposition - the matrix form. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // rotation using axis coordination
    void AxisCoordination(const Eigen::Vector3f &n, const Eigen::Vector3f &p, float angle) {
    double rotationAngle = angle / 180.0 * PI;

    auto startTime = std::chrono::high_resolution_clock::now();

    Eigen::Vector3f crossed = n.cross(p);
    Eigen::Vector3f u = n;
    Eigen::Vector3f v = crossed / crossed.norm();
    Eigen::Vector3f w = n.cross(v);
    Eigen::Matrix3f Q = Eigen::Matrix3f::Identity(), T = Eigen::Matrix3f::Identity();
    Q.row(0) = u;
    Q.row(1) = v;
    Q.row(2) = w;
    T << 1, 0, 0,
    0, cos(rotationAngle), -sin(rotationAngle),
    0, sin(rotationAngle), cos(rotationAngle);
    Eigen::Vector3f rotatedVector = Q.transpose() * T * Q * p;

    auto endTime = std::chrono::high_resolution_clock::now();
    double deltaTime = std::chrono::duration<double, std::milli>(endTime-startTime).count();

    cout << "Method: axis coordination. The rotated vector p' is ("
    << rotatedVector(0) << "," << rotatedVector(1) << "," << rotatedVector(2)
    << "). The time used is " << deltaTime << endl;
    }

    // main
    int main()
    {
    float angle = 60;
    Eigen::Vector3f p(1.0, 2.0, 3.0);
    Eigen::Vector3f n(2.0, 8.6, -3.1);
    n.normalize();

    // execute functions
    cout << "The vector p is (" << p(0) << "," << p(1) << "," << p(2) << "). "
    << "The rotation axis n is (" << n(0) << "," << n(1) << "," << n(2) << "). " << endl;
    DecomposeVector(n, p, angle);
    DecomposeMatrix(n, p, angle);
    AxisCoordination(n, p, angle);

    return 0;
    }
    输出是:
    The vector p is (1,2,3). The rotation axis n is (0.213724,0.919011,-0.331271). 
    Method: vector decomposition - the vector form. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.016354
    Method: vector decomposition - the matrix form. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.016642
    Method: axis coordination. The rotated vector p' is (3.57449,0.643966,0.899062). The time used is 0.021062
    再多试几组: +
    The vector p is (1,-654.1,12.88). The rotation axis n is (0.995044,0.0136933,-0.0984901). 
    Method: vector decomposition - the vector form. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.015503
    Method: vector decomposition - the matrix form. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.015297
    Method: axis coordination. The rotated vector p' is (-59.7309,-338.298,-556.777). The time used is 0.021547

    +
    The vector p is (32.5,45.1,-2.2). The rotation axis n is (0.57735,0.57735,0.57735). 
    Method: vector decomposition - the vector form. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.015558
    Method: vector decomposition - the matrix form. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.015215
    Method: axis coordination. The rotated vector p' is (5.16667,52.4667,17.7667). The time used is 0.020841
    +
    The vector p is (666,0,0). The rotation axis n is (0,0,1). 
    Method: vector decomposition - the vector form. The rotated vector p' is (1.78454e-05,666,0). The time used is 0.01517
    Method: vector decomposition - the matrix form. The rotated vector p' is (0,666,0). The time used is 0.015402
    Method: axis coordination. The rotated vector p' is (1.78454e-05,666,0). The time used is 0.020898
    +

    最后一组出现了精度问题,所以代码中还应该加入判断

    if(abs(value - round(value)) < epsilon)
    value = round(value);

    +

    从上面的例子来看,向量分解-向量形式向量分解-矩阵形式运行效率是一致的,而坐标轴对齐的效率较低,这主要是由计算三次矩阵乘法导致的。

    ]]>
    - 游戏 - 相机 + 数学 - 图形学 数学 随笔 - 相机 - 算法 - 游戏 + 计算机
    @@ -7048,31 +6989,131 @@ Lock,因此无法转化为对应的Euler角。从上面的过程来看,对 - 樱花 - /2021/06/29/00/24/ - 昨夜 我收到一封书札
    -旧纸上 缀满了一朵樱花
    -风干的墨迹 渗透了陌生人的迷茫
    -我慢慢 把信合上

    -

    昨夜 我写了一封书札
    -述说着 近日的苦闷衷肠
    -窗外的灯火 依旧那么辉煌
    -我贴上 一朵樱花珍藏

    -

    同样的夜晚 走过了岁月多少锋芒
    -心中仍 呐喊着一个熟悉人的回响
    -把信 随风寄向远方
    -留给陌生的他 打开新的过往
    -愿他不要怯懦 续写昨夜的感慨
    -还有清晨的阳光

    -

    案台旁 樱花在绽放

    + 根据角色体型和俯角自动确定FreeLook镜头参数 + /2022/06/21/23/40/ + 最近项目涉及到一个需求:如何自动确定镜头的参数(在Unity中即为轨道的高度和半径)。这个问题可以形式化为:给定角色身高、看向点偏移、俯角和人物在画面中的占高比,自动计算出相机到看向点的距离、相机中轨的半径和相机中轨的高度。本文推导出该计算公式,实现镜头参数自动化处理。

    + +

    角色高度、看向点偏移、俯角与相机参数

    +

    我们的第一步目标是:给定角色高度、看向点偏移和俯角,能够自动计算出相机距离、相机轨道的半径和高度。在此之前,首先需要明确几个概念:

    +
      +
    • 角色高度:角色模型在游戏中的实际高度,因为我们目前关心的角色在屏幕中的占高比,暂不考虑角色宽度;
    • +
    • 看向点偏移:从角色Root结点的Y轴偏移量,如果等于角色高度则位于角色头顶位置,等于零则位于Root位置;
    • +
    • 俯角:相机看向角色看向点时与XZ平面的夹角;
    • +
    • 相机距离:相机到看向点的距离;
    • +
    • 相机轨道高度:相机从角色Root点开始的Y轴高度;
    • +
    • 相机轨道半径:相机轨道在当前高度上的半径。
    • +
    +

    这几个参数的关系可以用下图来表示:

    +
    +几个参数之间的关系。X:角色高度,Y:看向点偏移,A:俯角,D:相机距离,H:相机高度,R:相机半径。 + +
    +

    我们不难得出下面的关系:

    +

    +

    但这是对的情况,当时,我们有:

    +

    +

    但是由于我们在计算俯角时,以下方向为正方向,当时,俯角实际上是负值,所以也不需要加负号。于是,最终的公式仍然是统一的。

    +

    人物高度、人物占高比、FOV与相机距离

    +

    上述公式的一个前提是需要知道可自定,),而我们实际上关注的是人物在整个画面中的占高比,而相机屏幕与FOV有关,所以为了计算,实际上就是要考虑通过FOV和人物高度、人物占高比计算出相机距离。

    +

    现在我们假定人物占高比固定为,可以根据需求自行推导。

    +
    +通过FOV和人物高度计算出相机距离。我们固定了1/3的人物占高比。 + +
    +

    如上图所示,相机投影的上半部分角度为FOV的一半,在这里我们假设FOV为,那么一半就是。设相机距离为,当角色高度的一半,即占屏幕上半的时,我们不难得到下述公式:

    +

    +

    但是上述公式没有考虑俯角,这就会导致一定的误差。下面我们分两种情况讨论,如下图所示:

    +
    +修正后的相机距离用俯角、FOV和人物高度计算得出。我们固定了1/3的人物占高比。 + +
    +

    角色半高在相机屏幕空间中的投影为。所以我们能够得到下述修正公式:

    +

    +

    该公式对两种情况都是适用的。

    +

    总结

    +

    总的来说,只要给定角色身高、看向点偏移(一般来说设置为或者)和俯角,在固定人物在画面中占高比和FOV为的前提下,就可以根据下述公式计算相机到看向点距离、相机中轨半径和相机中轨高度

    +

    +

    如果你需要改变人物在画面的占高比,或者相机的FOV,只需要按照上面的推导方法修改公式即可。

    +

    当然,你也可以继续精细化上述公式,如考虑实际的看向点偏移,但一般来说差别不是很大,在此不再赘述。

    ]]>
    - 随笔 + 游戏 - 相机 + 数学 + 随笔 + 相机 + 算法 + 游戏 + +
    + + 用生成函数和复数巧解一道计数题 + /2022/06/26/23/03/ + 对集合,求出其中有多少子集,使得子集中的元素和为5的倍数。本文对3Blue3Brown的解法上进行总结,形成较为数学化的语言。本题背后有较为深刻的数学思想,故记载于此。

    + +

    把原问题转化为生成函数

    +

    原题是:对集合,求出其中有多少子集,使得子集中的元素和为5的倍数。

    +

    首先容易想到的是,可以采用穷举法,遍历的每个子集,检查是否满足条件。但是,穷举法的复杂度是,这对计算机来说都是一个天文数字。显然,需要另辟蹊径。

    +

    不妨先简化一下题目:求出集合中有多少个子集,使元素之和为,我们可以很快地列出这些集合:

    +

    +

    换句话说,可以表示为:,如果我们把它表示成一个关于未知数的多项式,那么就有:

    +

    +

    什么情况会同时出现呢?答案是生成函数。这启发我们使用下面的生成函数:

    +

    +

    这个多项式展开之后的结果是:

    +

    +

    注意五次项前面的系数是3,这是因为,有三个来源,分别是,正好对应了之前简化题中的结果!

    +

    现在分析一下,多项式中的每个项,都代表了整数,如果在括号中选择了,则表示把这个整数加入集合,如果选择了,则表示不加入。而把整个多项式展开之后的结果,项前面的系数,正好代表了元素之和为的集合的个数!

    +

    这时候,我们就能把原问题转化为下面的生成函数:

    +

    +

    求出其中所有次数为的倍数的项系数之和:

    +

    +

    下面的问题就是如何求出

    +

    转化为复数问题

    +

    显然,我们不可能把中的每一项都求出来,只能设法一次求出,那么怎么办呢?

    +

    我们还是先考虑简化问题,假设现在生成函数是:

    +

    +

    如果我们代入,可以求得;如果代入,可以求得;再代入,有。用展开式表示结果:

    +

    +

    通过后面两式,不难得到:

    +

    +

    我们找到了所有奇数项系数之和,但是我们的目标是所有的倍数的项系数之和。但这个过程可以给我们一个启发:是否可以通过代入某些值到生成函数中,通过加减运算消去我们不关心的项,得到最终的结果

    +

    分析我们把代入到中每一项的过程,所有的偶数项结果都是,所有的奇数项结果都是。换句话说:这个值对函数的未知项的循环次数为,或者,每两项代入的结果都相同。同理,对来说,循环次数是

    +

    那么,什么时候循环次数是呢?也就是,代入哪个数,会使得,或者。显然,在复空间中,这个数就是。也就是说,如果令,则有个数满足我们的要求,其中就是我们熟知的实根。

    +

    更关键的是,这几个数的和是

    +

    +

    现在,分别把这五个值代入到展开的生成函数中,有:

    +

    +

    注意对应的项,正好都有,把它们加起来,有:

    +

    +

    是已知的,所以上面的结果就变成了:

    +

    +

    现在整个式子右边只留下常数和的倍数的项!

    +

    唯一遗留的问题就是式子左侧的是多少,或者说是多少,因为只要知道了的值也就知道了。

    +

    这里的技巧是,因为是方程的五个根,所以可以把它们表示成:

    +

    +

    代入:

    +

    +

    从而,我们也就有,而。所以,我们就得到了:

    +

    +

    这样一来我们考虑的简化版问题就得到了解决!

    +

    按照这个思路,我们同样可以得到原始问题的解决,分为几步。

    +

    第一步:设是方程的五个根,把它们分别代入生成函数中,相加,就得到:

    +

    +

    第二步:把表示为,通过代入解出

    +

    第三步:把代入到生成函数中,可以得到,同理有,同时有

    +

    第四步:把所有值代回第一步中的结果,得到最终答案

    +

    总结

    +

    本题背后蕴藏着较为深刻的数学思想,一方面是巧用生成函数把一个计数问题转化为一个分析问题,另一方面是使用复数消去某些项,直接求出想要的结果。通过本题,我们可以充分了解到复数所蕴含的关于周期和频率的思想,从而也就能理解为什么傅里叶变换用复数表征。

    +

    把本题稍微进行推广:对集合和正整数,其中。设中满足元素之和为的倍数的子集个数为,你能求出的表达式吗?

    +]]>
    + + 数学 - 分析 + + + 数学 随笔 - 生活 - 打油诗
    @@ -7192,72 +7233,31 @@ Designer画一个简单的行为树 - 用生成函数和复数巧解一道计数题 - /2022/06/26/23/03/ - 对集合,求出其中有多少子集,使得子集中的元素和为5的倍数。本文对3Blue3Brown的解法上进行总结,形成较为数学化的语言。本题背后有较为深刻的数学思想,故记载于此。

    - -

    把原问题转化为生成函数

    -

    原题是:对集合,求出其中有多少子集,使得子集中的元素和为5的倍数。

    -

    首先容易想到的是,可以采用穷举法,遍历的每个子集,检查是否满足条件。但是,穷举法的复杂度是,这对计算机来说都是一个天文数字。显然,需要另辟蹊径。

    -

    不妨先简化一下题目:求出集合中有多少个子集,使元素之和为,我们可以很快地列出这些集合:

    -

    -

    换句话说,可以表示为:,如果我们把它表示成一个关于未知数的多项式,那么就有:

    -

    -

    什么情况会同时出现呢?答案是生成函数。这启发我们使用下面的生成函数:

    -

    -

    这个多项式展开之后的结果是:

    -

    -

    注意五次项前面的系数是3,这是因为,有三个来源,分别是,正好对应了之前简化题中的结果!

    -

    现在分析一下,多项式中的每个项,都代表了整数,如果在括号中选择了,则表示把这个整数加入集合,如果选择了,则表示不加入。而把整个多项式展开之后的结果,项前面的系数,正好代表了元素之和为的集合的个数!

    -

    这时候,我们就能把原问题转化为下面的生成函数:

    -

    -

    求出其中所有次数为的倍数的项系数之和:

    -

    -

    下面的问题就是如何求出

    -

    转化为复数问题

    -

    显然,我们不可能把中的每一项都求出来,只能设法一次求出,那么怎么办呢?

    -

    我们还是先考虑简化问题,假设现在生成函数是:

    -

    -

    如果我们代入,可以求得;如果代入,可以求得;再代入,有。用展开式表示结果:

    -

    -

    通过后面两式,不难得到:

    -

    -

    我们找到了所有奇数项系数之和,但是我们的目标是所有的倍数的项系数之和。但这个过程可以给我们一个启发:是否可以通过代入某些值到生成函数中,通过加减运算消去我们不关心的项,得到最终的结果

    -

    分析我们把代入到中每一项的过程,所有的偶数项结果都是,所有的奇数项结果都是。换句话说:这个值对函数的未知项的循环次数为,或者,每两项代入的结果都相同。同理,对来说,循环次数是

    -

    那么,什么时候循环次数是呢?也就是,代入哪个数,会使得,或者。显然,在复空间中,这个数就是。也就是说,如果令,则有个数满足我们的要求,其中就是我们熟知的实根。

    -

    更关键的是,这几个数的和是

    -

    -

    现在,分别把这五个值代入到展开的生成函数中,有:

    -

    -

    注意对应的项,正好都有,把它们加起来,有:

    -

    -

    是已知的,所以上面的结果就变成了:

    -

    -

    现在整个式子右边只留下常数和的倍数的项!

    -

    唯一遗留的问题就是式子左侧的是多少,或者说是多少,因为只要知道了的值也就知道了。

    -

    这里的技巧是,因为是方程的五个根,所以可以把它们表示成:

    -

    -

    代入:

    -

    -

    从而,我们也就有,而。所以,我们就得到了:

    -

    -

    这样一来我们考虑的简化版问题就得到了解决!

    -

    按照这个思路,我们同样可以得到原始问题的解决,分为几步。

    -

    第一步:设是方程的五个根,把它们分别代入生成函数中,相加,就得到:

    -

    -

    第二步:把表示为,通过代入解出

    -

    第三步:把代入到生成函数中,可以得到,同理有,同时有

    -

    第四步:把所有值代回第一步中的结果,得到最终答案

    -

    总结

    -

    本题背后蕴藏着较为深刻的数学思想,一方面是巧用生成函数把一个计数问题转化为一个分析问题,另一方面是使用复数消去某些项,直接求出想要的结果。通过本题,我们可以充分了解到复数所蕴含的关于周期和频率的思想,从而也就能理解为什么傅里叶变换用复数表征。

    -

    把本题稍微进行推广:对集合和正整数,其中。设中满足元素之和为的倍数的子集个数为,你能求出的表达式吗?

    + 樱花 + /2021/06/29/00/24/ + 昨夜 我收到一封书札
    +旧纸上 缀满了一朵樱花
    +风干的墨迹 渗透了陌生人的迷茫
    +我慢慢 把信合上

    +

    昨夜 我写了一封书札
    +述说着 近日的苦闷衷肠
    +窗外的灯火 依旧那么辉煌
    +我贴上 一朵樱花珍藏

    +

    同样的夜晚 走过了岁月多少锋芒
    +心中仍 呐喊着一个熟悉人的回响
    +把信 随风寄向远方
    +留给陌生的他 打开新的过往
    +愿他不要怯懦 续写昨夜的感慨
    +还有清晨的阳光

    +

    案台旁 樱花在绽放

    ]]>
    - 数学 - 分析 + 随笔 - 数学 随笔 + 生活 + 打油诗
    diff --git a/sitemap.txt b/sitemap.txt index 7d792fb3..e9b1e739 100644 --- a/sitemap.txt +++ b/sitemap.txt @@ -1,3 +1,5 @@ +http://sulley.cc/about/index.html +http://sulley.cc/photos/index.html http://sulley.cc/photos/tour/index.html http://sulley.cc/photos/love/index.html http://sulley.cc/photos/tour/shanghai/index.html @@ -8,7 +10,6 @@ http://sulley.cc/resources/index.html http://sulley.cc/2022/03/27/08/38/ http://sulley.cc/2022/12/11/09/16/ http://sulley.cc/2022/03/25/01/00/ -http://sulley.cc/about/index.html http://sulley.cc/2023/05/03/18/22/ http://sulley.cc/photos/games/index.html http://sulley.cc/search/index.html @@ -34,7 +35,6 @@ http://sulley.cc/2020/12/22/09/29/ http://sulley.cc/2020/10/09/19/34/ http://sulley.cc/2020/09/08/18/39/ http://sulley.cc/comments/index.html -http://sulley.cc/photos/index.html http://sulley.cc/photos/moon/index.html http://sulley.cc/photos/seas/index.html http://sulley.cc/2020/10/18/16/03/ @@ -63,20 +63,20 @@ http://sulley.cc/tags/CDO/ http://sulley.cc/tags/%E7%BC%96%E8%BE%91%E5%99%A8/ http://sulley.cc/tags/%E5%8A%A8%E7%94%BB/ http://sulley.cc/tags/%E4%BD%9C%E4%B8%9A/ +http://sulley.cc/tags/%E5%B7%A5%E5%85%B7/ +http://sulley.cc/tags/%E7%BB%98%E7%94%BB/ +http://sulley.cc/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/ http://sulley.cc/tags/Unity/ http://sulley.cc/tags/Cinemachine/ http://sulley.cc/tags/Damping/ http://sulley.cc/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/ -http://sulley.cc/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/ -http://sulley.cc/tags/%E5%B7%A5%E5%85%B7/ -http://sulley.cc/tags/%E7%BB%98%E7%94%BB/ http://sulley.cc/tags/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/ -http://sulley.cc/tags/%E7%94%9F%E6%B4%BB/ -http://sulley.cc/tags/%E5%88%BA%E5%AE%A2%E4%BF%A1%E6%9D%A1/ http://sulley.cc/tags/Spline/ http://sulley.cc/tags/Curve/ http://sulley.cc/tags/%E6%9B%B2%E7%BA%BF/ http://sulley.cc/tags/%E6%8F%92%E5%80%BC/ +http://sulley.cc/tags/%E7%94%9F%E6%B4%BB/ +http://sulley.cc/tags/%E5%88%BA%E5%AE%A2%E4%BF%A1%E6%9D%A1/ http://sulley.cc/tags/%E6%8F%92%E4%BB%B6/ http://sulley.cc/tags/%E5%B7%AB%E5%B8%88%E4%B8%89/ http://sulley.cc/tags/%E8%AE%BE%E8%AE%A1/ @@ -91,6 +91,6 @@ http://sulley.cc/categories/%E9%9A%8F%E7%AC%94/ http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E7%8E%A9%E5%90%8E%E6%84%9F/ http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E7%90%86%E8%AE%BA/ http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E6%95%B0%E5%AD%A6%E5%88%86%E6%9E%90/ -http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E5%88%86%E6%9E%90/ http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E5%88%86%E6%9E%90/ +http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E5%88%86%E6%9E%90/ http://sulley.cc/categories/%E7%94%B5%E5%BD%B1-%E5%9B%BD%E4%BA%A7%E7%94%B5%E5%BD%B1/ diff --git a/sitemap.xml b/sitemap.xml index d3bc8e2c..bf7fef1f 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,6 +1,24 @@ + + http://sulley.cc/about/index.html + + 2023-07-28 + + monthly + 0.6 + + + + http://sulley.cc/photos/index.html + + 2023-07-27 + + monthly + 0.6 + + http://sulley.cc/photos/tour/index.html @@ -91,15 +109,6 @@ 0.6 - - http://sulley.cc/about/index.html - - 2023-07-25 - - monthly - 0.6 - - http://sulley.cc/2023/05/03/18/22/ @@ -325,15 +334,6 @@ 0.6 - - http://sulley.cc/photos/index.html - - 2022-03-27 - - monthly - 0.6 - - http://sulley.cc/photos/moon/index.html @@ -445,7 +445,7 @@ http://sulley.cc/ - 2023-07-27 + 2023-07-28 daily 1.0 @@ -453,238 +453,238 @@ http://sulley.cc/tags/%E6%95%B0%E5%AD%A6/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E9%9A%8F%E7%AC%94/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/UE/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E7%9B%B8%E6%9C%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E7%AE%97%E6%B3%95/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E6%A6%82%E7%8E%87%E8%AE%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E9%87%87%E6%A0%B7/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E8%93%84%E6%B0%B4%E6%B1%A0%E9%87%87%E6%A0%B7/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E6%B8%B8%E6%88%8F/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E8%93%9D%E5%9B%BE/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/CDO/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E7%BC%96%E8%BE%91%E5%99%A8/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E5%8A%A8%E7%94%BB/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E4%BD%9C%E4%B8%9A/ - 2023-07-27 + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/Unity/ - 2023-07-27 + http://sulley.cc/tags/%E5%B7%A5%E5%85%B7/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/Cinemachine/ - 2023-07-27 + http://sulley.cc/tags/%E7%BB%98%E7%94%BB/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/Damping/ - 2023-07-27 + http://sulley.cc/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/ - 2023-07-27 + http://sulley.cc/tags/Unity/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/ - 2023-07-27 + http://sulley.cc/tags/Cinemachine/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E5%B7%A5%E5%85%B7/ - 2023-07-27 + http://sulley.cc/tags/Damping/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E7%BB%98%E7%94%BB/ - 2023-07-27 + http://sulley.cc/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/ + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/ - 2023-07-27 + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E7%94%9F%E6%B4%BB/ - 2023-07-27 + http://sulley.cc/tags/Spline/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E5%88%BA%E5%AE%A2%E4%BF%A1%E6%9D%A1/ - 2023-07-27 + http://sulley.cc/tags/Curve/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/Spline/ - 2023-07-27 + http://sulley.cc/tags/%E6%9B%B2%E7%BA%BF/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/Curve/ - 2023-07-27 + http://sulley.cc/tags/%E6%8F%92%E5%80%BC/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E6%9B%B2%E7%BA%BF/ - 2023-07-27 + http://sulley.cc/tags/%E7%94%9F%E6%B4%BB/ + 2023-07-28 weekly 0.2 - http://sulley.cc/tags/%E6%8F%92%E5%80%BC/ - 2023-07-27 + http://sulley.cc/tags/%E5%88%BA%E5%AE%A2%E4%BF%A1%E6%9D%A1/ + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E6%8F%92%E4%BB%B6/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E5%B7%AB%E5%B8%88%E4%B8%89/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E8%AE%BE%E8%AE%A1/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E6%89%93%E6%B2%B9%E8%AF%97/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/tags/%E5%BD%B1%E8%AF%84/ - 2023-07-27 + 2023-07-28 weekly 0.2 @@ -693,84 +693,84 @@ http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E7%9B%B8%E6%9C%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E6%A6%82%E7%8E%87%E8%AE%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E5%9B%BE%E5%BD%A2%E5%AD%A6/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E5%BC%95%E6%93%8E/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E5%8A%A8%E7%94%BB/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E9%9A%8F%E7%AC%94/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E7%8E%A9%E5%90%8E%E6%84%9F/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E7%90%86%E8%AE%BA/ - 2023-07-27 + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E6%95%B0%E5%AD%A6%E5%88%86%E6%9E%90/ - 2023-07-27 + 2023-07-28 weekly 0.2 - http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E5%88%86%E6%9E%90/ - 2023-07-27 + http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E5%88%86%E6%9E%90/ + 2023-07-28 weekly 0.2 - http://sulley.cc/categories/%E6%95%B0%E5%AD%A6-%E5%88%86%E6%9E%90/ - 2023-07-27 + http://sulley.cc/categories/%E6%B8%B8%E6%88%8F-%E6%B8%B8%E6%88%8F%E5%88%86%E6%9E%90/ + 2023-07-28 weekly 0.2 http://sulley.cc/categories/%E7%94%B5%E5%BD%B1-%E5%9B%BD%E4%BA%A7%E7%94%B5%E5%BD%B1/ - 2023-07-27 + 2023-07-28 weekly 0.2