ggj ff.j_GGJ飞溅冲突–事后建模

ggj ff.j

Global Game Jam 2017 recently ended. It was a mad weekend where 36,000 game makers gathered in more than 700 venues around the world, and made 7000 games based on a common theme: “Waves”.

Global Game Jam 2017 最近结束了 。 那是一个疯狂的周末,全世界36,000个游戏制造商聚集在700多个场馆,并基于“ Waves”这一共同主题制作了7,000场游戏。

Instead of organising in Rome, this year I flew to Prague and made a game with a graphic artist named Jana Kilianová. Our game is called Splash Clash, and it’s a 2-player brawler on a tiny island, where two pixel characters jump to produce waves and bump each other out. This final concept is just one of the many we got during brainstorming: for instance, we toyed for a while with the idea of having players produce waves by making a sound in the microphone, but after some experiments we settled for this one – the one that seemed achievable in 48 hours.

今年,我没有去罗马组织,而是飞往布拉格,与一位名叫JanaKilianová的图形艺术家进行了比赛。 我们的游戏称为Splash Clash ,它是一个在狭窄小岛上的2人格斗游戏,其中两个像素字符跳跃以产生波浪并相互碰撞。 这个最终概念只是我们在头脑风暴中获得的众多概念之一:例如,我们玩了一段时间,以让玩家通过在麦克风中发出声音来产生波浪的想法,但是经过一些实验之后,我们决定解决这个问题–这似乎可以在48小时内实现。

波浪效应 (The wave effect)

From the very start we knew we wanted a cool and somehow believable water effect, so we didn’t want to use any approximation like 3D wave objects spreading in a circle, or particles. Instead, I thought we could use a circle texture on a Displacement shader, so the wave would be created from the surface (a simple plane) and if we then scaled this circle, we could have an animated wave.

从一开始,我们就知道我们想要一种酷炫的水效果,因此我们不希望使用任何近似方法,例如3D波浪形物体在圆周上散布或散布。 相反,我认为我们可以在“置换”着色器上使用圆形纹理,这样就可以从曲面(简单的平面)创建波浪,并且如果我们随后缩放该圆形,则可以有一个动画波浪。

Disclaimer: This post is intended to show you how I created the effect quickly during GGJ, not how it should be done! There are probably better ways in regards to performance! (perhaps doing it all with shaders?)

免责声明:这篇文章旨在向您展示我在GGJ期间如何快速创建效果,而不是应该怎么做! 关于性能,可能有更好的方法! (也许使用着色器完成所有操作?)

Before setting off, I made a mockup by creating circle textures in Photoshop and using the first sprite found on the web, Batman:

出发之前,我通过在Photoshop中创建圆形纹理并使用在网上找到的第一个精灵来制作模型,即蝙蝠侠:

Being no shader wizard, I found a very simple free Displacement shader in the actual Unity Manual, which also supports tesselation (so I wouldn’t have to worry about the geometry). Once applied to the water, the Material looks like this:

作为没有着色器向导的人,我在实际的Unity Manual中发现了一个非常简单的免费置换着色器,该着色器还支持镶嵌处理(因此,我不必担心几何形状)。 一旦应用于水中,该材料将如下所示:

As you can see it has a tessellation amount (that I pumped to the max!) – which controls how many times the surface is divided – and a displacement slider – which controls the height of the extrusion.

如您所见,它具有细分数量(我将其抽到最大!)–控制曲面被分割的次数–以及位移滑块–控制挤出的高度。

First thing, I wanted to verify that what I wanted to do was actually feasible. So I started with a single white circle texture (with transparency), then wrote a function to stamp it on top of a black texture. Once applied as a displacement map, it looks like this:

首先,我想验证我想做的事实际上是否可行。 因此,我从一个单一的白色圆圈纹理(具有透明度)开始,然后编写了一个将其标记在黑色纹理之上的功能。 一旦用作位移图,它看起来像这样:

Humble beginnings

卑微的开始

After that, I expanded the function to allow stamping more circles.

之后,我扩展了功能以允许冲压更多的圆。

Multiple still circles

多个静止圈

I had to composite multiple white circles together, to achieve having multiple waves moving on the surface. As you can see from the image above, the circles were quite high resolution at this point (I think they were 256×256 on a 512 black texture).

我必须将多个白色圆圈组合在一起,以实现在表面上移动多个波浪。 从上图可以看到,此时的圆圈分辨率很高(我认为它们在512黑色纹理上为256×256)。

Since each wave is independent, the whole texture has to be deleted each frame and recalculated with the scaled-up circles. Once animated, they looked like this:

由于每个波都是独立的,因此必须在每一帧删除整个纹理, 使用放大的圆重新计算 。 动画后,它们看起来像这样:

Random animated waves on a temporary colour texture

临时动画纹理上的随机动画波

At this point, I found out that I had to reduce the texture size, A LOT. The method I was using is not optimised at all: after all for a 512×512 bump texture it means 262,144 read and write pixel operations, for each wave! (remember: the waves were 256×256 but they would scale in time, needing to rewrite the whole thing each frame) This brings us to a grand total of 1,048,576 pixel operations every frame with just 4 waves on screen!

在这一点上,我发现我不得不减小纹理大小 ,很多。 我使用的方法根本没有优化:毕竟,对于512×512的凹凸纹理,对于每个波形,它意味着262,144个读写像素操作! (请记住:波形为256×256,但它们会随时间缩放,需要每帧重写整个内容),这使我们在屏幕上只有4个波形的情况下,每帧总共进行了1,048,576个像素的操作

So I scaled down everything, settling on a mere 32×32 for the ring textures and 64×64 for the whole bump. After a few tests, I ran into a problem: having a small texture, scaling it down (since the ring starts very small) and then scaling it up again destroys it until it’s not a circle anymore.

因此,我按比例缩小了所有内容,仅将环形纹理设置为32×32,将整个凹凸设置为64×64。 经过几次测试后,我遇到了一个问题:纹理较小,将其缩小(因为环开始很小),然后再次放大会破坏它,直到不再是一个圆为止。

So what I did is cache the original ring texture, and use that to re-generate the rescaled circle every frame, instead of just transforming one texture all the time (which would make it useless after a couple of scale-ups). This means that each wave has a reference to the original ring texture, but instead of using that it just copies the pixels onto the rescaled one.

因此,我要做的是缓存原始的环形纹理,并使用该纹理在每帧中重新生成缩放后的圆,而不是仅始终变换一个纹理(这会使它在放大几次后变得无用)。 这意味着每个波都有对原始环形纹理的引用,但不是使用 ,而是仅将像素复制到重新缩放的像素上

It worked fine on a plane, but when I applied the material to a circular disc shape I got this weird effect, with the sides of the disc extruding outwards:

它在平面上可以很好地工作,但是当我将材料应用于圆盘形状时,我得到了怪异的效果,使圆盘的侧面向外伸出:

See the edge extruding out on the left, leaving holes in the geometry?

看到边缘向左突出,在几何图形上留下Kong了吗?

I just had to apply one small modification to the shader: instead of extruding along the pixel normals, I would just extrude up. I changed this line:

我只需要对着色器进行一个小的修改:不用沿像素法线拉伸,而是将其拉伸。 我更改了这一行:

1

v.vertex.xyz += v.normal * d;

1

v . vertex . xyz += v . normal * d ;

… to this:

…对此:

1

v.vertex.xyz += float3(0,1,0) * d;

1

v . vertex . xyz += float3 ( 0 , 1 , 0 ) * d ;

Voilá, I had my waves finally working!

Voilá ,我的波浪终于奏效了!

Bump done, but waves are not really visible…

颠簸完成了,但是波浪并不真正可见…

Once the bump was done, it was time to take care of the colour texture. As you can see from the animation above, at first I had a shadow from the Directional light to highlight the waves’ profile, but it wasn’t enough at all. I needed something visible, something that the players could use as a feedback to time their jumps properly.

一旦完成凸起,就该照顾颜色纹理了。 从上面的动画中可以看到,起初,我在“方向性”灯光中有一个阴影来突出显示波浪的轮廓,但是这还远远不够。 我需要一些可见的东西,玩家可以将其用作反馈,以适当地安排他们的跳跃时间。

At first I basically applied the same trick again: white texture rings printed on top of the colour texture, exactly in the same position as the ones in the bump/displacement:

首先,我基本上再次应用了相同的技巧:将白色纹理环打印在颜色纹理的顶部,其位置与凹凸/位移处的位置完全相同:

In the above animation you can clearly see how the white circles display the low resolution of the textures used, especially when the circle is big.

在上面的动画中,您可以清楚地看到白色圆圈如何显示所用纹理的低分辨率,尤其是当圆圈较大时。

This is when I decided to modify the shader to paint the pixels at the top of the wave in white. After all it makes everything faster, since I cut half of the texture readings each frame.

这是我决定修改着色器以将波浪顶部的像素绘制为白色的时候。 毕竟,这使一切变得更快,因为我每帧都削减了一半的纹理读数。

I just changed the line that calculates the pixel color in the surf function of the shader, to something like:

我只是将着色器的surf函数中用于计算像素颜色的行更改为:

1

half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color * (30 * IN.worldPos.y + 1);

1

half4 c = tex2D ( _MainTex , IN . uv_MainTex ) * _Color * ( 30 * IN . worldPos . y + 1 ) ;

By using IN.worldPos.y, I make sure to take into account the height of the pixel in world space. The higher the pixel, the brighter it will be, tending towards white. The result is:

通过使用IN.worldPos.y,我确保将世界空间中像素的高度考虑在内。 像素越高,它将越亮,趋向于白色。 结果是:

物理 (Physics)

Once I had the graphics in place, it was time to make sure that the two characters got pushed by the wave. This was an easy one: I made each wave a gameObject with a Sphere Collider, marked as trigger. As the texture is scaled up, the Collider is scaled up as well.

当我放置好图形后,就该确保两个角色被浪潮推动了。 这很简单:我使每个挥手动作都带有一个Sphere Collider(标记为触发器)的gameObject。 随着纹理的放大,对撞机也随之放大。

The collider is scaled to match the wave on the displacement map

对撞机被缩放以匹配位移图上的波浪

Once it collides with one of the players (OnTriggerEnter), I just verify check the y coordinate of the character’s gameObject, and if it’s below a certain threshold (which is connected with the wave strength – a float that decreases in time) the character is pushed back by applying a force to its Rigidbody proportional – again – to the wave’s strength.

一旦它与其中一个玩家发生碰撞(OnTriggerEnter),我只需验证检查角色的gameObject的y坐标,并确定其是否低于某个阈值(与波浪强度相关联-随时间减小的浮点),则该角色就是通过对其刚体施加力来向后推,该力又与波浪的强度成比例。

I then filter the collisions by tag, so waves from player 1 only hit character 2, and vice versa.

然后,我按标签过滤碰撞,因此来自玩家1的波浪只会击中角色2,反之亦然。

Just a small mention regarding Drag. When going for a fully-physical approach, usually you apply a strong force (AddForce) to characters to make them accelerate quickly, then use Drag to balance that out so they don’t drift. But adding too much Drag will make the character descend slowly when jumping, creating some terribly floaty physics.

关于Drag的提及很少。 当采用全物理方法时,通常会向角色施加强大的力量(AddForce),以使它们快速加速,然后使用“拖动”将其平衡,以使它们不会漂移。 但是增加太多的阻力将使角色在跳跃时缓慢下降,从而产生一些非常漂浮的物理现象

In this case I implemented my own fake drag, by multiplying only the x and z components of the Rigidbodies’ velocity by an arbitrary factor, to keep the y component (the gravity) intact:

在这种情况下,我通过将刚体速度的x和z分量乘以任意因子来实现y分量(重力)不变,从而实现了自己的假阻力:

1

rb.velocity = new Vector3(rb.velocity.x * .8f, rb.velocity.y, rb.velocity.z * .8f);

1

rb . velocity = new Vector3 ( rb . velocity . x * . 8f , rb . velocity . y , rb . velocity . z * . 8f ) ;

场景设置 (The scene setup)

Finally, as you can see from the animation below, the game is a mix of 2D and 3D, with the characters being Sprites rotated 30º on the x axis to match the camera rotation, as are the backdrop and the rocks below the scenario. The disc of water is basically the only proper 3D object in the game, as I needed it to get complex and believable waves.

最后,从下面的动画中可以看到,该游戏是2D和3D的混合体,角色是Sprites在x轴上旋转了30º以适应相机的旋转,场景和场景下方的岩石也是如此。 水盘基本上是游戏中唯一合适的3D对象,因为我需要它来获得复杂且可信的波浪。

The characters have a Capsule Collider, and a Rigidbody whose rotation is locked so they just more around without tipping.

角色有一个Capsule Collider(胶囊对撞机)和Rigidbody(刚体),它们的旋转被锁定,因此它们可以在不倾翻的情况下保持更大的距离。

粒子 (Particles)

I have added particles to the jump to provide feedback on the jump, and to provide the so-called juicyness (remember Martin and Petri’s talk “Juice it or lose it”?):

我为跳跃添加了粒子,以提供有关跳跃的反馈,并提供所谓的多汁感(还记得马丁和皮特里(Martin and Petri)的演讲“榨汁还是输汁” ?):

For the waterfall particles, I just used a big Circle emitter and I separated the front particles from the back ones (which are spawned by another particle system) by putting it on 180º in the Shape module. The back ones don’t change in colour and have a way shorter life span, since they are never going to be seen.

对于瀑布粒子,我只使用了一个大的Circle发射器,然后将前粒子与后粒子(由另一个粒子系统生成)分开,并将其置于Shape模块中的180º处。 背面的颜色不变,使用寿命短,因为它们永远不会被看到。

As usual I randomize every parameter I can (lifetime, speed, gravity, etc.) as long as I’m using it, but in this case I kept the size fixed because I want the particles to have a pixel art feel. For the same reason I used a Fixed gradient in the Color over Lifetime volume instead of a Blend one, so they change colour instantly, instead of fading away as I would usually do with realistic particles.

像往常一样,只要可以使用,我都会随机分配每个参数(寿命,速度,重力等),但是在这种情况下,我将尺寸保持固定,因为我希望粒子具有像素艺术感。 出于相同的原因,我在“生命周期内的颜色”体积中使用了“固定”渐变,而不是“混合”体积,因此它们立即更改了颜色 ,而不是像我通常对逼真的粒子那样淡出。

闭幕 (Closing)

As you can see, most of the solutions I adopted this weekend feel dirty, unoptimised, and almost like a hack. That’s fine! Jam games ARE meant to be quick and dirty, they don’t need to be performing well.

如您所见,我这个周末采用的大多数解决方案都感到肮脏,未优化,并且几乎像黑客一样 。 没关系! Jam游戏本来应该既快又脏,但并不需要表现良好。

In fact when I have to decide what to spend my time on at a jam, I prefer to spend it on code quality instead of optimisation. I like to have a relatively solid approach at coding (say, less spaghetti code and more logical class structures and information flow) to avoid getting bugs at the last minute. This usually keeps me away from nasty, incomprehensible, last-minute bugs!

实际上,当我不得不决定花什么时间在卡纸上时,我宁愿将其花在代码质量上,而不是优化。 我喜欢在编码方面采用相对可靠的方法(例如,减少意大利面条式编码,增加逻辑类结构和信息流),以免在最后一分钟出现错误。 这通常使我远离令人讨厌,难以理解的最新错误!

翻译自: https://blogs.unity3d.com/2017/02/02/ggj-splash-clash-post-mortem/

ggj ff.j

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值