欢迎关注微信公众号「Swift 花园」

算术操作符

到目前为止,你已经了解了 Swift 的所有基本类型,现在让我们利用操作符把它们放在一起来使用。操作符指的是那些看起来像数学符号的玩意,比如 +- 。 Swift 中有大量的操作符。

下面有一些测试用的变量(这里特指数学里的变量,不局限于 Swift 的 var ):

1
2
let firstScore = 12
let secondScore = 4

让我们用 +- 把它们相加或者相减:

1
2
let total = firstScore + secondScore
let difference = firstScore - secondScore

我们还可以用 */ 来执行乘法和除法:

1
2
let product = firstScore * secondScore
let divided = firstScore /secondScore

Swift 有一个用于计算除法的余数的特殊操作符: % 。它可以计算一个数 A 用若干个数 B 填充后,剩余的空间。

举个例子,如果我们把 secondScore 设置为 4 ,那么当我们做 13 % secondScore 这个操作时,我们会得到 1 ,因为 4 可以填充 13 三次,余数为 1 :

1
let remainder = 13 % secondScore
阅读全文 »

欢迎关注微信公众号「Swift 花园」

数组

数组是用于存储一组值的合集。举个例子,红、绿、蓝是三基色,你可以对它们进行调和从而产生新颜色。因此颜色就可以用一个包含了红、绿、蓝三个值的数组来存储。

代码上,我们这么书写:

1
2
3
4
let red = 1.0
let green = 1.0
let blue = 1.0
let color = [red, green, blue]

上面的最后一行代码创建了一个数组:它以方括号开始和结束,里面的元素通过逗号分隔。

你可以通过书写数组名加上内部包含一个数字的方括号来读取数组里的元素。注意:这个数字代表元素在数组里的位置,几乎在所有的计算机编程语言中,这个位置都是从 0 开始的。所以,如果你想读取蓝色,可以这么写:

1
color [2]

小心:如果你尝试读取一个不存在的值, Swift 将会崩溃。例如,试图读取 color [5] 就不是一个好主意。

另外还要注意的是,如果你想使用类型注解,数组是通过方括号加里面的类型来表示的: [String][Int][Double][Bool]

阅读全文 »

欢迎关注微信公众号「Swift 花园」

变量

当你启动 Xcode 时,它会询问你想要做的事情,选择 “Get Started with a Playground” 。 Playground 是一个可以供你输入 Swift 代码并立即看到结果的沙盒环境。

默认的, Xcode 将提供一个 iOS 编程环境的空白 Playground, 点击 Next,然后再点击 Create ,将 Playground 保存到你的系统桌面。

在本文中,我将向你介绍 “变量”,它们是你用来存储程序数据的地方。之所以被称为 “变量”,是因为它们可变,即你可以自由地改变它们的值。

我们将从在 Playground 中的第一行代码开始了解 “变量”,它是一行创建了一个名称为 “str” , 值为 “Hello, playground” 的字符串变量的代码。

1
var str = "Hello, playground"

这行代码创建了一个名为 str 的新变量,并给它赋值为 “Hello, playground” 。在 Playground 右侧的输出区域你可以看到 “Hello, playground” – 这是 Xcode 在展示我们刚刚给 str 设置的值。

既然 str 是一个变量,我们就可以改变它:

1
str = "Goodbye"

第二次的时候,你不需要再写一次 var ,因为这个变量在前面已经被创建过了 – 我们只需要改变它。

阅读全文 »

Our team recently researched the special effects of “Flame Rain” and found many other developers’ solutions from the Internet. They were not very satisfactory for us, mainly because they could not meet the requirements of randomness and precise control at the same time. Finally, we decided to develop a component by ourselves.

We make use of the “machine gun” component which we develop previously. Machine guns can aim at targets and emit particle effects. The related API and prefabs support multiple targets, bullet prefabs, and the gun body can be rotated.

Our idea is to use many “machine guns” to build a “cloud”. This “cloud” can cause a regional “ball rain” attack on the ground.
Of course, this cloud is virtual, and it is not necessary to use render in the scene, it is only used to deploy machine guns. The machine gun is also invisible.

Here is the demo scene we used to verify the bullet cloud function during development:

Because the bullet cloud is usually invisible in the actual scene, you can deploy a bullet cloud anywhere. However, considering the time of flight of the particles, you need to decide the height of the cloud yourself. We implemented it as a well-encapsulated component. You can configure it with any number of raindrops to hit the target precisely (of course, there can be multiple targets), the amount of hit you want to happend on each target, which bullets you want to use. And, there are many other bullets which looks like randomly drop, but actually they are configured by us.

When deploying the bullet cloud to your own scene, you need to pay attention to check the particle effects used as bullets, so you can ensure that there is no layer which can collide with your target.

We applied the previously implemented particles of various projectiles as cloud bullets, which triggered explosions based on physical collisions. At the same time, we determine whether a certain rain process of the bullet cloud is completed based on the collision. This API is necessary for the design of the skill system. Because we need to know when the skills we have released are over.

Of course, if you want to use purely mathematical methods to determine whether the bullet hits the target, this part needs to be implemented by yourself. Our bullet cloud can’t do that yet.

Using this bullet cloud to achieve the effect of “Flame Rain”, we are quite satisfied, because it has achieved both randomly effect and accuratly control on the damage. Because many of our game’s skill look randomly, but their damage amount is fixed.

The implementation logic is still quite complicated, try it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
void EnsurePreciseHitDrop(int _preciseHitCount = -1)
{
PreciseHitCount = _preciseHitCount == -1 ? preciseHitCountPreset : _preciseHitCount;

if (PreciseHitCount > 0 && Targets != null && Targets.Length > 0)
{
PreciseHitDrop = new Aimer[PreciseHitCount];

if (PreciseHitCount == 1)
{
if (Targets.Length == 1)
{
PreciseHitDrop[0] = CenterDrop;
preciseDropMaxIndexInAllDrop = 0;
}
else
{
Vector3 target = Targets[Random.Range(0, Targets.Length)];

PreciseHitDrop[0] = AllDrop[1];
PreciseHitDrop[0].transform.position = DropMoveToAimAtTarget(target);

preciseDropMaxIndexInAllDrop = 1;
}
}
else if (PreciseHitCount > 1 && PreciseHitCount <= DropCount)
{
if (Targets.Length == 1)
{
PreciseHitDrop[0] = CenterDrop;
for (int i = 1; i < PreciseHitCount; i++)
{
PreciseHitDrop[i] = AllDrop[i];
PreciseHitDrop[i].transform.position = CenterDrop.transform.position;
}

preciseDropMaxIndexInAllDrop = PreciseHitCount - 1;
}
else if (Targets.Length > 1)
{
int targetCount = Targets.Length;
if (EquallyAssignPreciseHit)
{
int quota = PreciseHitCount / targetCount;
int remain = PreciseHitCount % targetCount;

int allDropIndex = 1;
int preciseDropIndex = 0;

for (int targetIndex = 0; targetIndex < targetCount; targetIndex++)
{
for (int i = 0; i < quota; i++)
{
PreciseHitDrop[preciseDropIndex] = AllDrop[allDropIndex];
PreciseHitDrop[preciseDropIndex].transform.position
= DropMoveToAimAtTarget(Targets[targetIndex]);

allDropIndex++;
preciseDropIndex++;
}
}

if (remain > 0)
{
for (int j = 0; j < remain; j++)
{
Vector3 target = Targets[Random.Range(0, Targets.Length)];

PreciseHitDrop[preciseDropIndex] = AllDrop[allDropIndex];
PreciseHitDrop[preciseDropIndex].transform.position
= DropMoveToAimAtTarget(target);

allDropIndex++;
preciseDropIndex++;
}
}
}
else
{
int allDropIndex = 1;
int preciseDropIndex = 0;

for (int i = 0; i < PreciseHitCount; i++)
{
Vector3 target = Targets[Random.Range(0, Targets.Length)];

PreciseHitDrop[preciseDropIndex] = AllDrop[allDropIndex];
PreciseHitDrop[preciseDropIndex].transform.position
= DropMoveToAimAtTarget(target);

allDropIndex++;
preciseDropIndex++;
}
}

preciseDropMaxIndexInAllDrop = PreciseHitCount;
}
}
else
{
preciseDropMaxIndexInAllDrop = DropCount - 1;

throw new System.InvalidOperationException("Precise hit count exceed total drop count.");
}
}
}