Cara Menghasilkan Efek Petir Mengejutkan 2D dengan Baik.
() translation by (you can also view the original English article)
Petir memiliki banyak kegunaan dalam permainan, mulai dari suasana latar belakang saat badai hingga serangan petir dahsyat dari seorang penyihir. Dalam tutorial ini, saya akan menjelaskan bagaimana memprogram untuk menghasilkan efek petir 2D yang mengagumkan: petir, cabang, dan bahkan teks.
catatan: Meskipun tutorial ini ditulis menggunakan C # dan XNA, anda harus bisa menggunakan teknik dan konsep yang sama di hamper pada semua lingkungan pengembangan game.
Pratinjau Video Akhir
Langkah 1: Buat Garis Bersinar
Blok bangunan dasar yang kita butuhkan untuk membuat petir adalah segmen garis. Mulailah dengan membuka perangkat lunak editing gambar favorit anda dan gambar garis lurus petir. Milik saya terlihat seperti ini:

Kita ingin menggambar garis dengan panjang yang berbeda, jadi kita akan memotong garis segmen menjadi tiga bagian seperti gambar di bawah ini. Ini akan memungkinkan kita untuk meregangkan segmen tengah ke panjang yang kita sukai. Karena kita akan meregangkan segmen tengah, kita bisa menyimpannya hanya dengan satu piksel tebal. Juga, karena potongan kiri dan kanan adalah bayangan cermin satu sama lain, kita hanya perlu menyimpan salah satunya. Kita bisa membalikkannya dengan kode.

Sekarang, mari kita buat kelas baru untuk menangani segmen garis gambar:
1 |
public class Line |
2 |
{
|
3 |
public Vector2 A; |
4 |
public Vector2 B; |
5 |
public float Thickness; |
6 |
|
7 |
public Line() { } |
8 |
public Line(Vector2 a, Vector2 b, float thickness = 1) |
9 |
{
|
10 |
A = a; |
11 |
B = b; |
12 |
Thickness = thickness; |
13 |
}
|
14 |
}
|
A dan B adalah titik akhir garis. Dengan menskalakan dan memutar potongan-potongan garis, kita bisa menggambar garis dengan ketebalan, panjang, dan orientasi. Tambahkan metode berikut ini Draw()
untuk kelas Line
(garis):
1 |
public void Draw(SpriteBatch spriteBatch, Color color) |
2 |
{
|
3 |
Vector2 tangent = B - A; |
4 |
float rotation = (float)Math.Atan2(tangent.Y, tangent.X); |
5 |
|
6 |
const float ImageThickness = 8; |
7 |
float thicknessScale = Thickness / ImageThickness; |
8 |
|
9 |
Vector2 capOrigin = new Vector2(Art.HalfCircle.Width, Art.HalfCircle.Height / 2f); |
10 |
Vector2 middleOrigin = new Vector2(0, Art.LightningSegment.Height / 2f); |
11 |
Vector2 middleScale = new Vector2(tangent.Length(), thicknessScale); |
12 |
|
13 |
spriteBatch.Draw(Art.LightningSegment, A, null, color, rotation, middleOrigin, middleScale, SpriteEffects.None, 0f); |
14 |
spriteBatch.Draw(Art.HalfCircle, A, null, color, rotation, capOrigin, thicknessScale, SpriteEffects.None, 0f); |
15 |
spriteBatch.Draw(Art.HalfCircle, B, null, color, rotation + MathHelper.Pi, capOrigin, thicknessScale, SpriteEffects.None, 0f); |
16 |
}
|
Disini, Art.LightningSegment
dan Art.HalfCircle
merupakan variabel statis Texture2D
yang memegang gambar dari potongan segmen garis. ImageThickness
diatur ke ketebalan garis tanpa cahaya. Dalam gambar saya, itu 8 piksel. Kita mengatur tutup asal ke sisi kanan, dan segmen tengah asal ke sisi kirinya. Ini akan membuat keduanya tergabung dengan mulus saat kita menggambar keduanya pada titik A. Segmen tengah diregangkan ke lebar yang diinginkan, dan tutup lainnya ditarik pada titik B, diputar 180 °.
Kelas XNA SpriteBatch
memungkinkan anda melewatinya , sebuah SpriteSortMode
dalam konstruktornya, yang menunjukkan urutan yang menggambarkan sprite. Saat anda menarik garis, pastikan untuk melewati SpriteBatch
dengan SpriteSortMode
yang diatur ke SpriteSortMode.Texture
. Hal ini untuk meningkatkan performa.
Kartu grafis (Graphics cards) sangat bagus saat menggambar tekstur yang sama berkali-kali. Namun, setiap kali mereka mengganti tekstur, ada overhead. Jika kita menggambar banyak garis tanpa menyortir, kita akan menggambar tekstur kita sesuai urutan ini:
LightningSegment, HalfCircle, HalfCircle, LightningSegment, HalfCircle, HalfCircle, ...
Hal ini berarti kita akan mengganti tekstur dua kali untuk setiap baris yang kita gambar. SpriteSortMode.Texture
mengatakan pada SpriteBatch
untuk menyortir panggilan Draw()
dengan tekstur sehingga semua LightningSegments
akan ditarik bersama dan semua HalfCircles
akan digambar bersama. Selain itu, saat kita menggunakan garis ini untuk membuat petir kilat, kita akan menggunakan campuran aditif untuk membuat cahaya dari potongan tumbukan dari kilat yang saling tumpang tindih saling digabungkan.
1 |
SpriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive); |
2 |
// draw lines
|
3 |
SpriteBatch.End(); |
Langkah 2: Garis Bergerigi (Jagged Lines)
Petir cenderung membentuk garis bergerigi, jadi kita memerlukan algoritma untuk menghasilkannya. Kita akan melakukan ini dengan memilih poin secara acak di sepanjang garis, dan menggeser mereka secara acak dari garis. Menggunakan perpindahan acak sepenuhnya cenderung membuat garis terlalu bergerigi, jadi kita akan memperlancar hasil dengan membatasi seberapa jauh titik-titik satu sama lain terdekat bias ditarik.

Garis dilipat dengan menempatkan titik pada offset yang sama ke titik sebelumnya; Hal ini memungkinkan garis secara keseluruhan menyimpang naik turun, sekaligus mencegah bagian darinya terlalu bergerigi. Inilah kodenya:
1 |
protected static List<Line> CreateBolt(Vector2 source, Vector2 dest, float thickness) |
2 |
{
|
3 |
var results = new List<Line>(); |
4 |
Vector2 tangent = dest - source; |
5 |
Vector2 normal = Vector2.Normalize(new Vector2(tangent.Y, -tangent.X)); |
6 |
float length = tangent.Length(); |
7 |
|
8 |
List<float> positions = new List<float>(); |
9 |
positions.Add(0); |
10 |
|
11 |
for (int i = 0; i < length / 4; i++) |
12 |
positions.Add(Rand(0, 1)); |
13 |
|
14 |
positions.Sort(); |
15 |
|
16 |
const float Sway = 80; |
17 |
const float Jaggedness = 1 / Sway; |
18 |
|
19 |
Vector2 prevPoint = source; |
20 |
float prevDisplacement = 0; |
21 |
for (int i = 1; i < positions.Count; i++) |
22 |
{
|
23 |
float pos = positions[i]; |
24 |
|
25 |
// used to prevent sharp angles by ensuring very close positions also have small perpendicular variation.
|
26 |
float scale = (length * Jaggedness) * (pos - positions[i - 1]); |
27 |
|
28 |
// defines an envelope. Points near the middle of the bolt can be further from the central line.
|
29 |
float envelope = pos > 0.95f ? 20 * (1 - pos) : 1; |
30 |
|
31 |
float displacement = Rand(-Sway, Sway); |
32 |
displacement -= (displacement - prevDisplacement) * (1 - scale); |
33 |
displacement *= envelope; |
34 |
|
35 |
Vector2 point = source + pos * tangent + displacement * normal; |
36 |
results.Add(new Line(prevPoint, point, thickness)); |
37 |
prevPoint = point; |
38 |
prevDisplacement = displacement; |
39 |
}
|
40 |
|
41 |
results.Add(new Line(prevPoint, dest, thickness)); |
42 |
|
43 |
return results; |
44 |
}
|
Kode itu mungkin terlihat sedikit mengintimidasi, tapi tidak begitu buruk begitu anda mengerti logikanya. Kita mulai dengan menghitung vektor normal dan garis singgung dari garis, bersama dengan panjangnya. Kemudian kita secara acak memilih sejumlah posisi di sepanjang garis dan menyimpannya dalam daftar posisi kita. Posisi diskalakan di antara keduanya 0
dan 1
dimana 0
merupakan awal garis dan 1
mewakili titik akhir. Posisi ini kemudian diurutkan agar kita dapat dengan mudah menambahkan segmen garis di antaranya.
Lingkaran melewati titik-titik yang dipilih secara acak dan menggesernya sepanjang normal dengan jumlah acak. Faktor skala ada untuk menghindari sudut yang terlalu tajam, dan amplop (envelope) memastikan kilat benar-benar menuju titik tujuan dengan membatasi perpindahan saat kita mendekati titik akhir.



Langkah 3: Animasi
Kilat harus berkedip terang dan kemudian memudar. Untuk mengatasinya, buatlah sebuah kelas LightningBolt
.
1 |
class LightningBolt |
2 |
{
|
3 |
public List<Line> Segments = new List<Line>(); |
4 |
|
5 |
public float Alpha { get; set; } |
6 |
public float FadeOutRate { get; set; } |
7 |
public Color Tint { get; set; } |
8 |
|
9 |
public bool IsComplete { get { return Alpha <= 0; } } |
10 |
|
11 |
public LightningBolt(Vector2 source, Vector2 dest) : this(source, dest, new Color(0.9f, 0.8f, 1f)) { } |
12 |
|
13 |
public LightningBolt(Vector2 source, Vector2 dest, Color color) |
14 |
{
|
15 |
Segments = CreateBolt(source, dest, 2); |
16 |
|
17 |
Tint = color; |
18 |
Alpha = 1f; |
19 |
FadeOutRate = 0.03f; |
20 |
}
|
21 |
|
22 |
public void Draw(SpriteBatch spriteBatch) |
23 |
{
|
24 |
if (Alpha <= 0) |
25 |
return; |
26 |
|
27 |
foreach (var segment in Segments) |
28 |
segment.Draw(spriteBatch, Tint * (Alpha * 0.6f)); |
29 |
}
|
30 |
|
31 |
public virtual void Update() |
32 |
{
|
33 |
Alpha -= FadeOutRate; |
34 |
}
|
35 |
|
36 |
protected static List<Line> CreateBolt(Vector2 source, Vector2 dest, float thickness) |
37 |
{
|
38 |
// ...
|
39 |
}
|
40 |
|
41 |
// ...
|
42 |
}
|
Untuk menggunakan ini, cukup buat LightningBolt
yang baru dan memanggil Update()
serta Draw()
setiap frame. Panggilan Update()
membuatnya memudar. IsComplete
akan memberitahu anda ketika petir telah sepenuhnya memudar keluar.
Anda sekarang dapat menarik petir anda dengan menggunakan kode berikut di kelas Game anda:
1 |
LightningBolt bolt; |
2 |
MouseState mouseState, lastMouseState; |
3 |
|
4 |
protected override void Update(GameTime gameTime) |
5 |
{
|
6 |
lastMouseState = mouseState; |
7 |
mouseState = Mouse.GetState(); |
8 |
|
9 |
var screenSize = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height); |
10 |
var mousePosition = new Vector2(mouseState.X, mouseState.Y); |
11 |
|
12 |
if (MouseWasClicked()) |
13 |
bolt = new LightningBolt(screenSize / 2, mousePosition); |
14 |
|
15 |
if (bolt != null) |
16 |
bolt.Update(); |
17 |
}
|
18 |
|
19 |
private bool MouseWasClicked() |
20 |
{
|
21 |
return mouseState.LeftButton == ButtonState.Pressed && lastMouseState.LeftButton == ButtonState.Released; |
22 |
}
|
23 |
|
24 |
protected override void Draw(GameTime gameTime) |
25 |
{
|
26 |
GraphicsDevice.Clear(Color.Black); |
27 |
|
28 |
spriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive); |
29 |
|
30 |
if (bolt != null) |
31 |
bolt.Draw(spriteBatch); |
32 |
|
33 |
spriteBatch.End(); |
34 |
}
|
Langkah 4: Cabang Petir
Anda bisa menggunakan kelas LightningBolt
sebagai blok bangunan untuk menciptakan efek petir yang lebih menarik. Misalnya, anda bisa membuat cabang petir seperti yang ditunjukkan di bawah ini:

Untuk membuat cabang petir, kita memilih titik acak di sepanjang petir dan menambahkan petir baru yang keluar dari titik-titik ini. Pada kode di bawah ini, kita membuat antara tiga dan enam cabang yang terpisah dari petir utama pada sudut 30°.
1 |
class BranchLightning |
2 |
{
|
3 |
List<LightningBolt> bolts = new List<LightningBolt>(); |
4 |
|
5 |
public bool IsComplete { get { return bolts.Count == 0; } } |
6 |
public Vector2 End { get; private set; } |
7 |
private Vector2 direction; |
8 |
|
9 |
static Random rand = new Random(); |
10 |
|
11 |
public BranchLightning(Vector2 start, Vector2 end) |
12 |
{
|
13 |
End = end; |
14 |
direction = Vector2.Normalize(end - start); |
15 |
Create(start, end); |
16 |
}
|
17 |
|
18 |
public void Update() |
19 |
{
|
20 |
bolts = bolts.Where(x => !x.IsComplete).ToList(); |
21 |
foreach (var bolt in bolts) |
22 |
bolt.Update(); |
23 |
}
|
24 |
|
25 |
public void Draw(SpriteBatch spriteBatch) |
26 |
{
|
27 |
foreach (var bolt in bolts) |
28 |
bolt.Draw(spriteBatch); |
29 |
}
|
30 |
|
31 |
private void Create(Vector2 start, Vector2 end) |
32 |
{
|
33 |
var mainBolt = new LightningBolt(start, end); |
34 |
bolts.Add(mainBolt); |
35 |
|
36 |
int numBranches = rand.Next(3, 6); |
37 |
Vector2 diff = end - start; |
38 |
|
39 |
// pick a bunch of random points between 0 and 1 and sort them
|
40 |
float[] branchPoints = Enumerable.Range(0, numBranches) |
41 |
.Select(x => Rand(0, 1f)) |
42 |
.OrderBy(x => x).ToArray(); |
43 |
|
44 |
for (int i = 0; i < branchPoints.Length; i++) |
45 |
{
|
46 |
// Bolt.GetPoint() gets the position of the lightning bolt at specified fraction (0 = start of bolt, 1 = end)
|
47 |
Vector2 boltStart = mainBolt.GetPoint(branchPoints[i]); |
48 |
|
49 |
// rotate 30 degrees. Alternate between rotating left and right.
|
50 |
Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(30 * ((i & 1) == 0 ? 1 : -1))); |
51 |
Vector2 boltEnd = Vector2.Transform(diff * (1 - branchPoints[i]), rot) + boltStart; |
52 |
bolts.Add(new LightningBolt(boltStart, boltEnd)); |
53 |
}
|
54 |
}
|
55 |
|
56 |
static float Rand(float min, float max) |
57 |
{
|
58 |
return (float)rand.NextDouble() * (max - min) + min; |
59 |
}
|
60 |
}
|
Langkah 5: Teks Petir
Berikut adalah video efek lain yang dapat anda temukan dari petir:
Pertama kita perlu mendapatkan piksel dalam teks yang ingin kita gambar. Kita melakukan ini dengan menarik teks ke sebuah RenderTarget2D
dan membaca kembali data pixel dengan RenderTarget2D.GetData<T>()
. Jika anda ingin membaca lebih lanjut tentang membuat efek partikel teks, saya memiliki tutorial lebih rinci di sini.
Kita menyimpan koordinat piksel dalam teks sebagai List <Vector2>
. Kemudian, setiap frame, kita secara acak mengambil sepasang titik-titik ini dan membuat ledakan petir di antara keduanya. Kita ingin merancangnya sehingga dua titik lebih dekat satu sama lain, semakin besar kesempatan kita membuat petir di antara keduanya. Ada teknik sederhana yang bisa kita gunakan untuk mencapai hal ini: kita akan memilih poin pertama secara acak, dan kemudian kita akan memilih sejumlah titik lain secara acak dan memilih yang terdekat.
Jumlah poin kandidat yang kita uji akan mempengaruhi tampilan teks petir; memeriksa sejumlah besar titik akan memungkinkan kita menemukan titik-titik yang sangat dekat untuk menarik petir di antara keduanya, yang akan membuat teks itu sangat rapi dan terbaca, namun dengan sedikit petir yang panjang di antara huruf. Angka yang lebih kecil akan membuat teks petir terlihat lebih menonjol tapi kurang terbaca.
1 |
public void Update() |
2 |
{
|
3 |
foreach (var particle in textParticles) |
4 |
{
|
5 |
float x = particle.X / 500f; |
6 |
if (rand.Next(50) == 0) |
7 |
{
|
8 |
Vector2 nearestParticle = Vector2.Zero; |
9 |
float nearestDist = float.MaxValue; |
10 |
for (int i = 0; i < 50; i++) |
11 |
{
|
12 |
var other = textParticles[rand.Next(textParticles.Count)]; |
13 |
var dist = Vector2.DistanceSquared(particle, other); |
14 |
|
15 |
if (dist < nearestDist && dist > 10 * 10) |
16 |
{
|
17 |
nearestDist = dist; |
18 |
nearestParticle = other; |
19 |
}
|
20 |
}
|
21 |
|
22 |
if (nearestDist < 200 * 200 && nearestDist > 10 * 10) |
23 |
bolts.Add(new LightningBolt(particle, nearestParticle, Color.White)); |
24 |
}
|
25 |
}
|
26 |
|
27 |
for (int i = bolts.Count - 1; i >= 0; i--) |
28 |
{
|
29 |
bolts[i].Update(); |
30 |
|
31 |
if (bolts[i].IsComplete) |
32 |
bolts.RemoveAt(i); |
33 |
}
|
34 |
}
|
Langkah 6: Optimalisasi
Teks petir, seperti yang ditunjukkan di atas, dapat berjalan dengan lancar jika anda memiliki komputer papan atas, tapi ini tentu sangat berat. Setiap petir berlangsung lebih dari 30 frame, dan kita membuat lusinan petir baru setiap frame. Karena setiap petir mungkin memiliki beberapa segmen garis, dan setiap segmen garis memiliki tiga bagian, kita akhirnya menggambar banyak sprite. Demo saya, misalnya, menarik lebih dari 25.000 gambar setiap frame dengan pengoptimalan dimatikan. Kita bisa membuat yang lebih baik.
Alih-alih menggambar setiap petir sampai habis, kita bisa menarik setiap petir baru ke target render dan memudarkan target render setiap frame. Ini berarti, daripada harus menarik setiap petir untuk 30 frame atau lebih, kita hanya akan menggambar sekali. Ini juga berarti tidak ada kinerja tambahan untuk membuat petir kita memudar lebih lambat dan bertahan lebih lama.
Pertama, kita akan memodifikasi kelas LightningText
hanya untuk menarik setiap petir untuk satu frame. Di kelas game
anda, nyatakan dua variabel RenderTarget2D
: currentFrame
dan lastFrame
pada LoadContent ()
, instal mereka seperti ini:
1 |
lastFrame = new RenderTarget2D(GraphicsDevice, screenSize.X, screenSize.Y, false, SurfaceFormat.HdrBlendable, DepthFormat.None); |
2 |
currentFrame = new RenderTarget2D(GraphicsDevice, screenSize.X, screenSize.Y, false, SurfaceFormat.HdrBlendable, DepthFormat.None); |
Perhatikan format permukaan yang disetel HdrBlendable
. HDR merupakan singkatan dari High Dynamic Range, dan ini menunjukkan bahwa permukaan HDR kita dapat mewakili rentang warna yang lebih besar. Hal ini diperlukan karena memungkinkan target render untuk memiliki warna yang lebih terang dari putih. Bila banyak petir tumpang tindih, kita memerlukan target render untuk menyimpan jumlah keselurhan dari warnanya, yang mungkin bertambah melampaui kisaran warna standar. Sementara warna yang lebih terang dari putih ini tetap ditampilkan sebagai warna putih di layar, penting untuk menyimpan kecerahan penuh darinya agar bisa membuatnya memudar dengan baik.
Setiap frame, pertama kita menarik isi frame terakhir ke frame saat ini, namun sedikit gelap. Kita kemudian menambahkan petir yang baru dibuat ke frame saat ini. Akhirnya, kita membuat frame kita saat ini ke layar, dan kemudian menukar dua target render sehingga untuk frame berikutnya, lastframe
akan mengacu pada frame yang baru saja kita render.
1 |
void DrawLightningText() |
2 |
{
|
3 |
GraphicsDevice.SetRenderTarget(currentFrame); |
4 |
GraphicsDevice.Clear(Color.Black); |
5 |
|
6 |
// draw the last frame at 96% brightness
|
7 |
spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp, null, null); |
8 |
spriteBatch.Draw(lastFrame, Vector2.Zero, Color.White * 0.96f); |
9 |
spriteBatch.End(); |
10 |
|
11 |
// draw new bolts with additive blending
|
12 |
spriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive); |
13 |
lightningText.Draw(); |
14 |
spriteBatch.End(); |
15 |
|
16 |
// draw the whole thing to the backbuffer
|
17 |
GraphicsDevice.SetRenderTarget(null); |
18 |
spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp, null, null); |
19 |
spriteBatch.Draw(currentFrame, Vector2.Zero, Color.White); |
20 |
spriteBatch.End(); |
21 |
|
22 |
Swap(ref currentFrame, ref lastFrame); |
23 |
}
|
24 |
|
25 |
void Swap<T>(ref T a, ref T b) |
26 |
{
|
27 |
T temp = a; |
28 |
a = b; |
29 |
b = temp; |
30 |
}
|
Langkah 7: Variasi Lainnya
Kita telah membahas bagaimana membuat cabang petir dan teks petir, tapi itu tentu bukan satu-satunya efek yang bisa anda buat. Mari kita lihat beberapa variasi lain pada petir yang mungkin bisa anda gunakan.
Petir yang Bergerak
Seringkali anda mungkin ingin membuat kilatan petir yang bergerak. Anda bisa melakukan ini dengan menambahkan petir pendek baru setiap frame pada titik akhir dari frame petir sebelumnya.
1 |
Vector2 lightningEnd = new Vector2(100, 100); |
2 |
Vector2 lightningVelocity = new Vector2(50, 0); |
3 |
|
4 |
void Update(GameTime gameTime) |
5 |
{
|
6 |
Bolts.Add(new LightningBolt(lightningEnd, lightningEnd + lightningVelocity)); |
7 |
lightningEnd += lightningVelocity; |
8 |
|
9 |
// ...
|
10 |
}
|
Petir yang Halus
Anda mungkin telah memperhatikan bahwa kilat menyala lebih terang pada persendian. Hal ini disebabkan pencampuran aditif. Anda mungkin ingin petir lebih halus, lebih banyak lagi petir yang rata. Hal ini dapat dilakukan dengan mengubah fungsi wilayah campuran anda untuk memilih nilai maksimal dari warna sumber dan tujuan, seperti yang ditunjukkan di bawah ini.
1 |
private static readonly BlendState maxBlend = new BlendState() |
2 |
{
|
3 |
AlphaBlendFunction = BlendFunction.Max, |
4 |
ColorBlendFunction = BlendFunction.Max, |
5 |
AlphaDestinationBlend = Blend.One, |
6 |
AlphaSourceBlend = Blend.One, |
7 |
ColorDestinationBlend = Blend.One, |
8 |
ColorSourceBlend = Blend.One |
9 |
};
|
Lalu, di fungsi Draw()
, panggil SpriteBatch.Begin()
dengan maxBlend
sebagai BlendState
daripada BlendState additive
. Gambar di bawah menunjukkan perbedaan antara campuran aditif dan campuran maksimal pada petir.






Tentu max blending tidak akan membiarkan cahaya dari beberapa petir atau dari background untuk ditambahkan dengan baik. Jika anda ingin petir itu sendiri terlihat mulus, tapi juga mencampur aditif dengan petir lainnya, anda bisa membuat petir pertama ke target render dengan menggunakan blending maksimal, lalu tarik target render ke layar menggunakan aditif. Berhati-hatilah untuk tidak menggunakan terlalu banyak target render besar karena ini akan mengurangi kinerja.
Alternatif lain, yang akan bekerja lebih baik untuk sejumlah besar petir, adalah menghilangkan cahaya yang tertanam pada gambar segmen garis dan menambahkannya kembali dengan menggunakan efek cahaya pasca-pemrosesan. Rincian penggunaan shader dan efek cahaya di luar cakupan tutorial ini, namun anda bisa menggunakan Sampel Bloom XNA untuk memulai. Teknik ini tidak akan memerlukan lebih banyak target render saat anda menambahkan lebih banyak petir.
Kesimpulan
Petir adalah efek khusus yang bagus untuk menyegarkan permainan anda. Efek yang dijelaskan dalam tutorial ini adalah titik awal yang bagus, tapi pastinya tidak semua hal bisa anda lakukan dengan petir. Dengan sedikit imajinasi anda bisa membuat segala macam efek petir yang menakjubkan! Download kode sumbernya dan buatlah eksperimen anda sendiri.
Jika Anda menikmati artikel ini, lihatlah tutorial tentang efek air 2D juga.