Article for Gamer

XNA Game วิธีแก้ไขให้ ชุดเริ่มต้น เลื่อนฉากได้

หลายคนที่โหลดชุดพัฒนา XNA Game Studio 3.1 มาจะเห็นมามันแถม Platform Stater Kit Source Code ของเกมแนว Platform Side Scrolling (ตลุยด่านแบบด้านข้าง) และลอง Compile ดูจะเห็นว่ามันใช้งานได้ 100% แต่ติดตรงทำไมเรา Edit Level แล้วทำมัย ตัวเกมมันถึง Fixed หน้าจอเดินไปขวาหรือซ้านยสุดก็ชนขอบ ทำไงจะให้ ไปทางซ้ายหรือฉากก็เลื่อนซ้ายขวาต่อ หากมีอาการคันไม้คันมือไม่รู้จะเริ่มยังไง ผมไป ศึกษามาให้แล้วครับ

ก่อนอื่นเลย หากเครื่องของคุณยังไม่ลง XNA Studio 3.0 หรือ 3.1 ให้ไปดาวน์โหลดมาติดตั้ง ก่อนนะครับ
ที่เวป www.xna.com เลยครับ หาไม่เข้าใจก็ไปอ่านบทความย้อนหลังได้ที่นี่ครับ XNA Studio 3.1 คลอดแล้ว!
และ ถ้าอยากรู้ว่า Platform Starter Kit เกมที่แถมมากับชุดพัฒนาเป็นยังไงก็ไปอ่านบทความที่นี่ก่อนครับ
XNA Starter Kit: Platformer แล้วจะรู้ครับ และเป็นประโยชน์ที่จะเริ่ม

ผมได้ไปอ่าน บทความการใช้งาน Library ของ XNA จากที่นี่ครับ http://msdn.microsoft.com
เค้าได้อธิบายอย่างชัดเจนเลยว่าการจะให้มัน Scrolling ฉากหลังแบบ Layer นั้นทำได้ และต้อง Debug
ชุด code เล็กน้อยเท่านั้นเองครับ

เริ่มเลยครับ ให้เราไปเปิดไฟล์โปรเจ็ค ชื่อ PlatformerGame.cs
แล้วทำการแก้ไข code เล็กน้อยครับ โดยไปที่ ฟังก์ชั่น ชื่อว่า Draw แล้วทำการ remove หรือ ลบ code สอง บรรทัดนี้
บรรทัดแรกเลยคือ

spriteBatch.Begin();

และบรรทัดที่สองคือ

spriteBatch.End();

ตามตัวอย่างครับ

protected override void Draw(GameTime gameTime) 

{

graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

//spriteBatch.Begin();  --->ลบอกไป

level.Draw(gameTime, spriteBatch);

DrawHud();

//spriteBatch.End(); ---> ลบออกไป

base.Draw(gameTime);

}

 

โอเคครับหลังจากนั้น เราก็ไป ที่ ฟังก์ชั่นที่ชื่อว่า DrawHud ครับแล้วไปเพิ่ม Code ที่หน้าตาคล้าย
สองรรทัดที่แล้ว ที่ตรง บรรทัดแรกใรฟังก์ชั่นและ บรรทัดสุดท้ายตอนปิด ฟังก์ชั่น ครับ

ตามตัวอย่าง บรรทัดนี้ ใครที่หา ฟังก์ชั้นไม่เจอก็ทำตามภาพครับ

private void DrawHud() 

{

//Nuy Debug ส่วนที่เพิ่มมา

spriteBatch.Begin();

//Nuy Debug ส่วนที่เพิ่มมา

Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;

Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y);

Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,

titleSafeArea.Y + titleSafeArea.Height / 2.0f);

.....

//Nuy Debug ส่วนที่เพิ่มมา

spriteBatch.End();

//Nuy Debug ส่วนที่เพิ่มมา

}

 

เมื่อเสร็จขั้นตอนนี้แล้วครับ ก็ไปเปิด ไฟล์ที่ชื่อว่า Level.cs ที่โปรเจ็คครับ


ให้ไปแก้ไข Code ที่บรรทัดนี้


private Layer[] layers;

ให้แก้ไขเป็น

private float cameraPosition;

เมื่อจัดการเสร้จแล้วไปที่ ฟังก์ชั่นที่ชื่อว่า Layer ครับ ทำการ Comment code ชุดเก่า แล้ว replace ชุดใหม่เข้าไปดัง ตัวอย่างครับ

public Level(IServiceProvider serviceProvider, string path)

{

// Create a new content manager to load content used just by this level.

content = new ContentManager(serviceProvider, "Content");timeRemaining = TimeSpan.FromMinutes(2.0);

LoadTiles(path);

// Load background layer textures. For now, all levels must

// use the same backgrounds and only use the left-most part of them.

 

 

/*เริ่มต้นทำการคอมเม็น code ชุดนี้

layers = new Texture2D[3];

for (int i = 0; i < layers.Length; ++i)

{

// Choose a random segment if each background layer for level variety.

int segmentIndex = random.Next(3);

layers[i] = Content.Load<Texture2D>("Backgrounds/Layer" + i + "_" + segmentIndex);

}

/*สิ้นสุดทำการคอมเม็น code ชุด

// Load sounds.

exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached");

}

 

แล้วทำการ เพิ่ม Code ชุดข้างล่างนี้ลงไปครับ 
layers = new Layer[3]; 

layers[0] = new Layer(Content, "Backgrounds/Layer0", 0.2f);layers[1] =

new Layer(Content, "Backgrounds/Layer1", 0.5f);layers[2] = new Layer(Content, "Backgrounds/Layer2", 0.8f);

จะได้ ดังตัวอย่าง ทั้งฟังก์ชั่นดัง รูปแบบด้านล่างครับ

public Level(IServiceProvider serviceProvider, string path) 

{

// Create a new content manager to load content used just by this level.

content = new ContentManager(serviceProvider, "Content");timeRemaining = TimeSpan.FromMinutes(2.0);

LoadTiles(path);

// Load background layer textures. For now, all levels must

// use the same backgrounds and only use the left-most part of them.

/*Nuy Debug

layers = new Texture2D[3];

for (int i = 0; i < layers.Length; ++i)

{

// Choose a random segment if each background layer for level variety.

int segmentIndex = random.Next(3);

layers[i] = Content.Load<Texture2D>("Backgrounds/Layer" + i + "_" + segmentIndex);

}*/

layers = new Layer[3];

layers[0] = new Layer(Content, "Backgrounds/Layer0", 0.2f);layers[1] =

new Layer(Content, "Backgrounds/Layer1", 0.5f);layers[2] = new Layer(Content, "Backgrounds/Layer2", 0.8f);

// Load sounds.

exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached");

}

อ่ะใจเย็นอย่าเพิ่ง งง กันนะครับ ยังไปได้อีก

ในไฟล์ Level.cs นี่แหละครับให้ นำ Code ชุดข้างล่างนี้ไปวางแทนที่ ฟังก์ชั่น Draw  ชุดเก่าเลยครับ

public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
      spriteBatch.Begin();
      for (int i = 0; i <= EntityLayer; ++i)
        layers[i].Draw(spriteBatch, cameraPosition);
      spriteBatch.End();

      ScrollCamera(spriteBatch.GraphicsDevice.Viewport);
      Matrix cameraTransform = Matrix.CreateTranslation(-cameraPosition, 0.0f, 0.0f);
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None, cameraTransform);

      DrawTiles(spriteBatch);

      foreach (Gem gem in gems)
        gem.Draw(gameTime, spriteBatch);

      Player.Draw(gameTime, spriteBatch);

      foreach (Enemy enemy in enemies)
        enemy.Draw(gameTime, spriteBatch);

      spriteBatch.End();

      spriteBatch.Begin();
      for (int i = EntityLayer + 1; i < layers.Length; ++i)
        layers[i].Draw(spriteBatch, cameraPosition);
      spriteBatch.End();
    }

 

แล้วไปจัดการที่ ฟังก์ชั่น DrawTiles  ครับ บรรทัดแรกของฟังก์ชั่นให้แทรก Code ด้านล่าง

// Calculate the visible range of tiles.
    int left = (int)Math.Floor(cameraPosition / Tile.Width);
    int right = left + spriteBatch.GraphicsDevice.Viewport.Width / Tile.Width;
    right = Math.Min(right, Width - 1);

 

แล้วไปที่ loop ครับ จะเห็น loop ที่มี loop ซ้อนให้นำ code ด้านล่างนี้ไปวางที inner Loop หรือ Loop X ภายในครับ

for (int x = left; x <= right; ++x)

เสร็จแล้วให้คุณ เขียนฟังก์ชั่นขึ้นมาใหม่ครับ ตาม code fuction ชุดนี้เลย

private void ScrollCamera(Viewport viewport)
    {
#if ZUNE
const float ViewMargin = 0.45f;
#else
const float ViewMargin = 0.35f;
#endif

      // Calculate the edges of the screen.
      float marginWidth = viewport.Width * ViewMargin;
      float marginLeft = cameraPosition + marginWidth;
      float marginRight = cameraPosition + viewport.Width - marginWidth;

      // Calculate how far to scroll when the player is near the edges of the screen.
      float cameraMovement = 0.0f;
      if (Player.Position.X < marginLeft)
        cameraMovement = Player.Position.X - marginLeft;
      else if (Player.Position.X > marginRight)
        cameraMovement = Player.Position.X - marginRight;

      // Update the camera position, but prevent scrolling off the ends of the level.
      float maxCameraPosition = Tile.Width * Width - viewport.Width;
      cameraPosition = MathHelper.Clamp(cameraPosition + cameraMovement, 0.0f, maxCameraPosition);
    }

 

หนุกหนานพอยังครับมันยังไม่จบ Hilight ของ งานนี้โปล่มาแล้ว เราต้องทำการ Add Class
ใน Project ของเราครับ ตามรูปเลยให้ Add Class ใหม่ขึ้นมาครับ

แล้วให้ตั้งชื่อว่า Layer.cs ครับ

ซึ่งในไฟล์นี้ บน Header ให้คุณใส่ Code ด้านล่าง

using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
และประกาศ properties ให้กับ Class นี้ครับ ดังตัวอย่งด้านล่าง
public Texture2D[] Textures { get; private set; } 
public float ScrollRate { get; private set; }
แล้วตามด้วย Source code function 2 อันนี้ก็เป็นอันเสร็จประการครับ
 public Layer(ContentManager content, string basePath, float scrollRate)
    {
      // Assumes each layer only has 3 segments.
      Textures = new Texture2D[3];
      for (int i = 0; i < 3; ++i)
        Textures[i] = content.Load<Texture2D>(basePath + "_" + i);

      ScrollRate = scrollRate;
    }
 
public void Draw(SpriteBatch spriteBatch, float cameraPosition)
    {
      // Assume each segment is the same width.
      int segmentWidth = Textures[0].Width; // Calculate which segments to draw and how much to offset them.
      float x = cameraPosition * ScrollRate;
      int leftSegment = (int)Math.Floor(x / segmentWidth);
      int rightSegment = leftSegment + 1;
      x = (x / segmentWidth - leftSegment) * -segmentWidth;

      spriteBatch.Draw(Textures[leftSegment % Textures.Length], new Vector2(x, 0.0f), Color.White);
      spriteBatch.Draw(Textures[rightSegment % Textures.Length], new Vector2(x + segmentWidth, 0.0f), Color.White);
    }

 

วิธีทดลองอยากรู้ว่าเกมเราจะ เดินไปสุดจอด้านขวาแล้วฉากจะเลื่อนให้มั้ยก็ไปเปิดไฟล์ Level0.txt ที่

Folder \HighResolutionContent\Levels\0.txt ครับแล้วลองใส่ Sample data ตามข้างล่างนี้ ระวัง! ตรวจสอบดีๆนะครับ

ห้ามมีการเคาะบรรทัดจนเกิด 16 บรรทัดหล่ะ Compile ไม่ผ่านอย่ามาโวยวายนะจ๊ะ

……………………………………………………………………

……………………………………………………………………

………………………G……………………………………….X…

……………………..###……………………………….############

………………….G……………………………………………….

…………………###…………….G.GDG………….G.G.G……………

……………..G………………..#########……….#######…………..

…………….###…………………………………………………..

…………G……………….G.G……………G.G…………………….

………..###……………..#####………….#####……………………

…….G…………………………………………………………….

……###………………………….GDG.G………….G.G.G.G.G.G.G.G.G.G.

………………………………..#########………##.G.G.G.G.G.G.G.G.G..

.1………………………………………………..GCG.G.G.GCG.G.G.GCG.

####################………………………………..####################

เห็นมั้ย สนุกไม่ยากหากใฝ่รู้ ส่วนใครที่ทำยังไง๊ยังไง ก็ไม่ผ่านอาจจะขาดทักษะ ก็ดาวน์โหลด Project
ที่ผมทำไว้แล้ว run ได้ 100%

ไป Rebuild และ Compile ใหม่ดีกว่าเลย
ดาวน์โหลดที่นี่ครับ https://www.daydev.com/project/xna/lession1.zip

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Leave a Reply

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน