Apžvelgsime, kaip nesudėtingai ir greitai galima pasidaryti dinaminį apšvietimą 2D žaidime naudojant XNA. Be šeiderių bei be papildomų resursų. Tikslas yra graži žaidimo scena su naktiniu mėnuliu, tamsiu fonu apšviestu žibintais bei pradiniu fonu ir žaidimo meniu. Šviesos šaltinių skaičius neapribotas, o fonas savavališkas. Apšviestas tik pradinis fonas. Pageidaujama turėti elementarias žinias dirbant su XNA.
Pradžioje keletas žodžių teorijos ir paprastų pavyzdžių. Nupiešiam keletą kvadratų pilkame fone:
GraphicsDevice.Clear(Color.Black); spriteBatch.Begin(); spriteBatch.Draw(box1, Vector2.Zero, Color.White); spriteBatch.Draw(box2, new Vector2(40, 50), Color.White); spriteBatch.Draw(box4, new Vector2(150, 50), Color.White); spriteBatch.Draw(box3, new Vector2(260, 50), Color.White); spriteBatch.End();
Čia mes nuspalviname ekraną juoda spalva. Vėliau piešiame pilką stačiakampį box1 ir ant viršaus 3 kvadratus: box2, box3 ir box4. Dabar pabandome šiek tiek pakeisti metodo spriteBatch.Begin() parametrus, o tiksliau:
spriteBatch.Begin(SpriteSortMode.BackToFront, blendState);
Kintamajį blendState atnaujiname taip:
var blendState = BlendState.Additive;
Arba taip. Kurio rezultatas bus tas pats:
var blendState = new BlendState(); blendState.AlphaBlendFunction = BlendFunction.Add; blendState.AlphaDestinationBlend = Blend.One; blendState.AlphaSourceBlend = Blend.SourceAlpha; blendState.BlendFactor = Color.White; blendState.ColorBlendFunction = BlendFunction.Add; blendState.ColorDestinationBlend = Blend.One; blendState.ColorSourceBlend = Blend.SourceAlpha; blendState.ColorWriteChannels = ColorWriteChannels.All; blendState.ColorWriteChannels1 = ColorWriteChannels.All; blendState.ColorWriteChannels2 = ColorWriteChannels.All; blendState.ColorWriteChannels3 = ColorWriteChannels.All; blendState.MultiSampleMask = -1;
Gauname tokį rezultatą:
Iš tiesų mes deklaruojame, kad piešdami box2 ant box1 viršaus reikia nepridengti taškus, o juos sumaišyti pagal spalvą. Sumaišymas vykdomas pagal formulę:
- (source * sourceBlendFactor) blendFunction (destination * destinationBlendFactor)
- (box2.RGB * BlendState.ColorSourceBlend) BlendFunction.Add (box1.RGB * BlendState.ColorDestinationBlend)
- (box2.RGB * Blend.SourceAlpha) + (box1.RGB * BlendState.One)
- (box2.RGB) + (box1.RGB
Kitaip sakant, jeigu pas jus yra 2 taškai su vienodomis koordinatėmis ir spalvomis R:10 G:20 B:255 ir R:1 G:2 B:255, tai atstojamasis taškas bus gautas su spalva R:11 G:22 B:255, t.y. taps šviesesnis. Iš to galime gauti rezultatą, kad žaidžiant su klasės BlendState nustatymais galima patamsinti ir pašviesinti atskirus objekto plotus, taip gaunant norimą 2D apšvietimo efektą. Kad nedaug apšviesti apskritą objekto plotą reikia nupiešti apskritą tamsiai pilką objektą ant viršaus. Jei atvirkščiai, padaryti apskritą plotą tamsiu, reikia vėl nupiešti apskritą tamsiai pilką objektą, bet naudoti funkciją BlendFunction.ReverseSubtract.
Taigi baigiame teoriją ir pereiname prie praktikos. Kad gauti gražią sceną, kaip pavaizduota pirmame paveikslėlyje atliksime šiuos etapus:
- Ruošiame objektą su galiniu planu. Tam visą galinį planą piešiame viename objekte (RenderTarget2D);
- Ruošiame objektą su priekiniu planu;
- Ruošiame objektą su šešėliais. Tam imame objektą iš antrojo punkto ir šviesos šaltinių taškuose piešiame juodus apskritus objektus - tai sritis, kuri nebus tamsi.
Dabar visa tai išvedame į ekraną:
- Piešiame naktinį mėnulį;
- Piešiame objektą su galiniu planu;
- Dar kart piešiame objektą su galiniu planu, bet su -0.9 koeficientu. Gauname tamsų galinį planą;
- Piešiame priekinį planą;
- Piešiame objektą su šešėliais, koeficientas -0.9;
- Piešiame meniu.
Rezultatas:
Ir galiausiai galutinis kodas:
// 1. Ruošiame objektą su galiniu planu. GraphicsDevice.SetRenderTarget(backgroundSprite); GraphicsDevice.Clear(Color.Transparent); spriteBatch.Begin(); spriteBatch.Draw(background, Vector2.Zero, Color.White); spriteBatch.End(); // 2. Ruošiame objektą su priekiniu planu. GraphicsDevice.SetRenderTarget(foregroundSprite); GraphicsDevice.Clear(Color.Transparent); spriteBatch.Begin(); spriteBatch.Draw(foreground, Vector2.Zero, Color.White); spriteBatch.End(); // 3. Ruošiame objektą su šešėliais. GraphicsDevice.SetRenderTarget(foregroundShadow); GraphicsDevice.Clear(Color.Black); spriteBatch.Begin(); spriteBatch.Draw(foregroundSprite, Vector2.Zero, Color.White); spriteBatch.Draw(light, new Vector2(620, 490), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.Draw(light, new Vector2(100, 500), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.Draw(light, new Vector2(620, 90), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.Draw(light, new Vector2(290, 270), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.Draw(light, new Vector2(400, 270), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.Draw(light, new Vector2(510, 270), null, Color.White, 0.0f, new Vector2(light.Width / 2, light.Height / 2), 1.0f, SpriteEffects.None, 0.0f); spriteBatch.End(); // Dabar visa tai išvedame į ekraną. GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); // 1. Piešiame naktinį mėnulį. // 2. Piešiame objektą su galiniu planu. spriteBatch.Begin(); spriteBatch.Draw(sky, Vector2.Zero, Color.White); spriteBatch.Draw(backgroundSprite, Vector2.Zero, Color.White); spriteBatch.End(); // Ruošiame objektą BlendState. var blendState = new BlendState(); blendState.AlphaBlendFunction = BlendFunction.ReverseSubtract; blendState.AlphaDestinationBlend = Blend.One; blendState.AlphaSourceBlend = Blend.BlendFactor; // Paslaptingasis koeficientas -0.9 (255 * 0.9 = 230, BlendFunction.ReverseSubtract = -1) { blendState.BlendFactor = new Color(230, 230, 230, 255); blendState.ColorBlendFunction = BlendFunction.ReverseSubtract; } blendState.ColorDestinationBlend = Blend.One; blendState.ColorSourceBlend = Blend.BlendFactor; blendState.ColorWriteChannels = ColorWriteChannels.All; blendState.ColorWriteChannels1 = ColorWriteChannels.All; blendState.ColorWriteChannels2 = ColorWriteChannels.All; blendState.ColorWriteChannels3 = ColorWriteChannels.All; blendState.MultiSampleMask = -1; // 3. Dar kart piešiame objektą su galiniu planu, bet su koeficientu -0.9. spriteBatch.Begin(SpriteSortMode.BackToFront, blendState); spriteBatch.Draw(backgroundSprite, Vector2.Zero, Color.White); spriteBatch.End(); // 4. Piešiame priekinį planą. spriteBatch.Begin(); spriteBatch.Draw(foregroundSprite, Vector2.Zero, Color.White); spriteBatch.End(); // 5. Piešiame objektą su šešėliais, koeficientas -0.9. spriteBatch.Begin(SpriteSortMode.BackToFront, blendState); spriteBatch.Draw(foregroundShadow, Vector2.Zero, Color.White); spriteBatch.End(); // 6. Piešiame meniu. spriteBatch.Begin(); spriteBatch.Draw(menu, Vector2.Zero, Color.White); spriteBatch.End();
Paruoštą projektą ant Visual Studio 2010 galite parsisiųsti iš čia: xnagames.codeplex.com/releases/view/136161
Pasiremta: habrahabr.ru
Komentarų nėra:
Rašyti komentarą