418383 การเขียนโปรแกรมเกม User Interface Programming

Preview:

DESCRIPTION

418383 การเขียนโปรแกรมเกม User Interface Programming. ประมุข ขันเงิน pramook@gmail.com. Game User Interface. Game User Interface. Game User Interface. เราต้องการ control ลักษณะคล้ายกับของโปรแกรมประยุกต์ทั่วไป ปุ่มกด radio button check box drop down list box sliders ฯลฯ. - PowerPoint PPT Presentation

Citation preview

418383 การเขยนโปรแกรมเกมUser Interface Programming

ประมข ขนเงนpramook@gmail.com

Game User Interface

Game User Interface

Game User Interface

• เราตองการ control ลกษณะคลายกบของโปรแกรมประยกตทวไป– ปมกด– radio button– check box– drop down list box– sliders– ฯลฯ

วธการสราง User Interface

• เขยนเองทกอยาง–

• ใชไลบรารทคนอนสรางไว– ใช WinForms• Control อยขางนอก XNA• เหมาะสำาหรบใชเวลาสราง editor ตางๆ

– ใชไลบรารสำาหรบสราง User Interface ในเกมโดยเฉพาะ• Control อยใน XNA• เหมาะสำาหรบใชสราง user interface ในเกม

การใช XNA กบ WINFORMS

การทำางานของ XNA ปกต• เรา subclass Game• Game สรางหนาตางหนงหนาตางสำาหรบเกมของคณ

• เรา override เมธอด Update และ Draw

• บางกรณเราอาจพบวา framework นมขอจำากด– บางครงคณอยากจะสรางหนาตางเอง– บางครงคณอาจจะอยากสราง editor สำาหรบเกม

Microsoft.Xna.Framework.Game

• XNA ประกอบดวย assembly หลายๆ ตว– Microsoft.Xna.Framework– Microsoft.Xna.Framework.Graphics– Microsoft.Xna.Framework.Content– ฯลฯ

• Microsoft.Xna.Framework.Game มคลาส Game ทเปนโคดระดบสงทใช assembly

อน ในการสราง application สำาหรบเกมโดยเฉพาะ

การรนโคดเกมดวยวธอน• คณตองสรางคลาสททำาหนาทแทน Game• ในทนเราจะสราง control ทโคด XNA สามารถวาดรปใสได

• แลวเอา control นไปเปนสวนประกอบของ โปรแกรมทใช Windows Forms ธรรมดา

• โคดตวอยางทอยในWinFormsGraphicsSample_4_0

จะมคลาส GraphicsDeviceControl สำาหรบทำาหนาทนโดยเฉพาะ

GraphicsDeviceControl

• สบเชอสายมาจากSystem.Windows.Form.Cotnrol– มความสามารถในการวาดรปตวเองบนหนาจอ– ใช GraphicsDevice ของ XNA ในการวาดรป

• สามารถใชมนใน WinForms Designer ได– แตเวลาใชจะวาดรปตวเองเปนพนทวางสฟาเฉยๆ

สรางโปรแกรม Windows Forms ทใช XNA ได

• สราง Windows Forms Application แทนการสรางXNA Game

• เพม Reference ตอไปน– Microsoft.Xna.Framework– Microsoft.Xna.Framework.Graphics

• คดลอกไฟล– GraphicsDeviceControl.cs– GraphicsDevice.cs– ServiceContainer.cs

สรางโปรแกรม Windows Forms ทใช XNA ได

• สราง subclass ของ GraphicsDeviceControl ใหม โดย override เมธอด

– Initialize– Draw

การจดการ GRAPHICS DEVICE

การจดการ GraphicsDevice

• ในโปรแกรมหนงๆ อาจม GraphicsDeviceControl อยไดหลายๆ ตว

• แตเพอใหโปรแกรมมประสทธภาพ ควรจะมGraphicsDevice เพยงแคตวเดยวเทานน

GraphicsDeviceService

• คลาสททำาการจดการ GraphicsDevice• สรางมนขนมาเพยงตวเดยว แลวแจกจายให

GraphicsDeviceControl หลายๆ ตวใช

• มเมธอด AddRef สำาหรบใชเพมGraphicsDeviceControl ตวใหม– เมอ AddRef ถกเรยกครงแรกจะสราง

GraphicsDevice ขนมาใหม– แตเมอถกเรยกครงตอไปจะไมสรางใหมให

GraphicsDeviceService

• มเมธอด Release สำาหรบใหGraphicsDeviceControl เรยกเมอมนจะเลกใชGraphicsDevice– ถาเมอเรยกแลวจำานวน GraphicsDeviceControl ทใช

เปน 0 แลว GraphicsDeviceService จะทำาลายGraphicsDevice ทง

GraphicsDevice ขนาดแตกตางกน• เมอม GraphicsDeviceControl หลายๆ ตวแตละตวอาจมพนทแตกตางกน

• แต GraphicsDevice มเพยงแคตวเดยว และ GraphicsDeviceControl ทกตวตองใชมนรวม

กน• แลวเจา GraphicsDevice ตวนควรจะม back

buffer ขนาดเทาใดด?

GraphicsDevice ขนาดแตกตางกน• โคดตวอยางจะกำาหนดขนาด back buffer ใหม

ขนาดใหญกวา GraphicsDeviceControl ทใหญทสด

• เวลา GraphicsDeviceControl ตางๆ จะใชGraphicsDevice “ ” มนจะตองกำาหนด หนาตาง ในGraphicsDevice ทมนจะใชกอนเรยกใชจรง

• หลงจากวาดรปใส GraphicsDevice เสรจแลวก ตอง copy ขอมลเฉพาะสวนทสนใจนำาไปแสดงผล

BeginDraw Draw EndDraw

• วาดรปตวเองโดยของ GraphicsDeviceControl จงแบง เปนสามขนตอน และแตละขนตอนมเมธอดของตวเอง

• GraphicsDeviceControl.BeginDraw– กำาหนดขนาดของ GraphicsDevice ใหใหญกวาพนทตวเอง– กำาหนดบรเวณของ GraphicsDevice ทจะใชจรง (Viewport)

• GraphicsDeviceControl.Draw– วาดรปตวเองตามทผใชตองการ

• GraphicsDevice.EndDraw– เรยก GraphicsDevice.Present เพอเอารปทวาดใส

GraphicsDevice ไปแสดงผลในบรเวณของตวเอง

การอาน CONTENT ในWINFORMS APPLICATION

อาน Content ใน WinForms App

• ถาคณตองการอาน content ทสรางดวย content pipeline คณตองทำาสองอยางตอไปน– Content ทคณจะใชจะตองถก build ไปพรอมกบ

project ของคณ– ตองสราง ContentManager

Build Content พรอมกบ App

• สราง content project ใหมโดยเลอกAdd New Item Empty Content Project

• ใส content ทคณตองการใชใน content project ทสราง

• แต content project ไมสามารถ build ตวเองได• คณตองสราง XNA Game Library ขนมาอนหนง• แลวเพม content project เขาใน game library โดย

เพมมนเขาใน Content Reference ของ game library• หลงจากนนจงเพม reference ของ game library

ลงใน application

สราง ContentManager

• ContentManager ตองการ GraphicsDevice สำาหรบ อานขอมลทางกราฟกสตางๆ ( เชน Texture2D)

• เวลา ContentManager ตองการใช GraphicsDevice มนจะไปเรยกหา GraphicsDevice จากคลาสท

implement IGraphicsDeviceService• ใน GraphicsDeviceControl จะม property Services ท

เราสามารถไปดง IGraphicsDeviceService ออกมาได• ดวธการสราง ContentManager ใน constructor ของ

SpriteFontControl

อนเมชนใน WINFORMS APPLICATION

อนเมชนใน WinForms Application

• Application ทเขยนดวย Windows Forms ทวไปจะไมมภาพเคลอนไหว– Control ตางๆ สวนใหญจะอยนง– จะถกปลกใหตนขนมาเพอวาดรปตวเองใหมเมอผใช

ปอน input• ตางกบเกม– ตองมการวาดรปใหมอยอยางตอเนอง– เกมใน XNA สวนใหญจะวาดรปแบบน

• วธการวาดรปของ WinForms กบของ XNA จงเขากนไมได

เมอใช XNA กบ WinForms

• เมอคณสราง GraphicsDeviceControl แลวจะตองเลอกวาจะใชวธการวาดรปตวเองแบบไหน

• ในโคดตวอยางม control ทใชวธการวาดทงสองแบบ– SpriteFontControl ใชวธวาดแบบ WinForms– TriangleControl ใชวธวาดแบบ XNA

วธวาดแบบ WinForms

• หากตองการใชวธวาดแบบ WinForms เวลา subclass GraphicsDeviceControl

กไมตองเขยนโคดเพมเตม– แค override Initialize กบ Draw ตามปกต– Draw จะถกเรยกเมอ control จะตองวาดตวเองโดยอตโนมต

วธการวาดแบบ WinForms

• ถาหากตองการให control ทำาการวาดตวเองใหม ทกๆ ชวงเวลาทกำาหนด ให

– สราง Timer ไวสำาหรบจบเวลาทผานไป– ตงคา Timer.Interval ใหตรงกบชวงเวลาทตองการ– สรางเมธอดทจะถกเรยกเมอ Timer ตสญญาณเวลา– แลวเพมเมธอดนนเขาใน event Timer.Tick– หลงจากนนเรยก Timer.Start()

• โคดขางบนทงหมดควรใสไวใน constructor ของsubclass ของ GraphicsDeviceControl ทคณเขยน

วธการวาดแบบ WinForms

• โคดตวอยางtimer = new Timer();timer.Interval =

(int)TargetElapsedTime.TotalMilliseconds;timer.Tick += Tick;timer.Start();

วธการวาดแบบ WinForms

• ขอด– เขากบสวนอนของระบบ UI ไดดทสด– สามารถเอาเวลาระหวางท Timer ตสญญาณไปใชทำาอยางอนได

– ใชงานไดดถาม control หลายตวทตองทำาอนเมชนพรอมกน

• ขอเสย– อาจไมไดภาพเคลอนไหวทลนไหลทสด– Timer อาจตสญญาณชากวากำาหนดถาระบบกำาลงทำางานหนก

– Timer มความแมนยำาจำากด

วธการวาดแบบ XNA (1)

• SpinningTriangleControl ทำาการวาดรปบอยทสด เทาทจะทำาไดโดยการเพม handle ใส event

Application.Idle– Application.Idle += Tick;

• Application.Idle จะถกปลอยออกมาดวย Win32 message queue ทกครงท message queue วาง

• ถาจะใชเมธอดนในการวาดรป เราจะตองเรยก เมธอด Invalidate ใน Tick

วธการวาดแบบ XNA (1)

• กลไกการทำางาน– Win32 message queue ยง Application.Idle – Invalidate ถกเรยก– Invalidate สง WM_PAINT ไปยง control– WM_PAINT ถกประมวลผล ทำาให control วาดตวเองใหม– ไมม message คางใน queue ทำาให Application.Idle ถกเรยกใหม

• ถาใน event handler ไมมการเรยก Invalidate แลว จะม Application.Idle สงมาเพยงครงเดยวเทานน

วธการวาดแบบ XNA (1)

• ขอด– เขยนโคดสน

• ขอเสย– อาจไมไดอนเมชนทลนไหลทสด เนองจากมชวงเวลา

ระหวางท Application.Idle ถกสงกบการวาดรปจรง

วธการวาดแบบ XNA (2)

• เราสามารถทำาใหโปรแกรมของเราเชค Win32 message queue อยางตอเนอง เพอใหตอบสนอง

ตอ message ไดอยางรวดเรวยงขน• ขนแรกใหใส event handler ใหมให

Application.Idle– Application.Idle += TickWhileIdle;

• TickWhileIdle จะเขา infinite loop แลวเรยก Tick ทนทเมอ message queue วาง

วธการวาดแบบ XNA (2) void TickWhileIdle(object sender, EventArgs e) { NativeMethods.Message message;

while (!NativeMethods.PeekMessage(out message, IntPtr.Zero, 0, 0, 0))

{ Tick(sender, e); } }

วธการวาดแบบ XNA (2)

• NativeMethods.PeekMessage เปนเมธอดทเรา เขยนขนมาเพอดวาม message ใน Win32

message queue หรอไม

วธการวาดแบบ XNA (2) static class NativeMethods { [StructLayout(LayoutKind.Sequential)] public struct Message { public IntPtr hWnd; public uint Msg; public IntPtr wParam; public IntPtr lParam; public uint Time; public System.Drawing.Point Point; }

[DllImport("User32.dll"] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool PeekMessage(out Message message,

IntPtr hWnd, uint filterMin, uint filterMax, uint flags); }

วธการวาดแบบ XNA (2)

• ขอด– ไดอนเมชนทลนไหลทสด

• ขอเสย– เขากบระบบสวนอนไมไดเนองจากแยงเวลาเขาไปใชหมด

– เอาเวลาการทำางานของ CPU ไปใช 100%– เกดปญหาถาม control ทตองทำาอนเมชนพรอมๆ กน

เมธอด Tick

• เพอใหอนเมชนลนไหล เมธอด Tick จะตองทำาการupdate สถานะของเกมและวาดรปในอตราทคงท

• เทคนคการวาดทงสองแบบของเราทผานไมไดรบประกนอตราการวาด

• ดงนนใน Tick จะตองมการเชคนาฬกาวาเวลาผานไปเทาไหรแลว

แลวจงเรยก update เพอเวลาผานไปมากกวาคาบ ทตองทำาการ update

เมธอด Tick Stopwatch stopWatch = Stopwatch.StartNew(); readonly TimeSpan TargetElapsedTime =

TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 60); readonly TimeSpan MaxElapsedTime =

TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 10);

TimeSpan accumulatedTime; TimeSpan lastTime;

เมธอด Tick void Tick(object sender, EventArgs e) { TimeSpan currentTime = stopWatch.Elapsed; TimeSpan elapsedTime = currentTime - lastTime; lastTime = currentTime;

if (elapsedTime > MaxElapsedTime) elapsedTime = MaxElapsedTime; accumulatedTime += elapsedTime;

bool updated = false; while (accumulatedTime >= TargetElapsedTime) { Update(); accumulatedTime -= TargetElapsedTime; updated = true; }

if (updated) Invalidate(); }

IN-GAME USER INTERFACE

ไลบรารสำาหรบสราง User Interface

• ขณะนมไลบรารสำาหรบสราง user interface ใน XNA หลายตว– xWinForms (http://sourceforge.net/projects/xwinforms/)– BlackStar GUI (http://blackstar.codeplex.com/)– XNA Simple GUI (http://simplegui.codeplex.com/)– Windows System for XNA (http://wsx.codeplex.com/)– Nuclex Framework (

http://nuclexframework.codeplex.com/)• Library อนๆ– หาขอมลเพมไดจาก

http://forums.create.msdn.com/forums/t/15274.aspx

Nuclex Framework

• เราจะใช Nuclex Framework– เขยนสำาหรบ XNA 4.0– มการ update เมอไมนานมาน

• โปรเจคอนๆ หลายตวรสกจะเลกทำากนไปหลงจากป 2009 กเยอะ• การใช Nuclex Framework– เวบใชแนะนำาใหดาวนโหลด source code แลวเพมโปรเจคทตองการเขาในเกมของคณ

– ในกรณการทำา user interface เราจะใช• Nuclex.Input• Nuclex.UserInterface

GuiManager

• Nuclex.GuiManager เปนคลาสทใชในการแสดง ผล GUI control ทงหมด

– คณสามารถเพม control “ ” อนๆ ใหเปน ลก ของGuiManager

– เมอ GuiManager.Draw ถกเรยก มนจะวาดลกของมนทงหมด

• GuiManager เปน DrawableGameComponent

DrawableGameComponent

• คลาส DrawableGameComponent เปนกลไลของXNA ในการแยกเกมออกเปนสวนๆ ทเปนอสระจากกน– สามารถเอาไปเพมใหกบ Game เพอใหเกมมความสามารถใหม

– สามารถใช DrawableGameComponent เดยวกนกบ เกมหลายๆ เกมได

DrawableGameComponent

• เวลาสราง DrawableGameComponent จะตองoverride เมธอดตอไปน– Initialize– LoadContent– UnloadContent– Update– Draw

• คลายๆ กบสราง Game และ Screen ใหม

DrawableGameComponent

• DrawableGameComponent กบ Screen ไมเหมอนกน– ณ เวลาหนงจะม Screen เพยงแค Screen เดยว– “ ” คณ เปลยน Screen– ในเวลาหนงจะม DrawableGameComponent มากกวาหนงตว

– “ ” คณ เพม DrawableGameComponent เมอตองการใช

– “ ” คณ ลบ DrawableGameComponent เมอเลกใช

DrawableGameComponent

• สงทเรามกจะได DrawableGameComponent ทำา– เคอรเซอรของเมาส– User Interface

• ผมไมคอยชอบใช DrawableGameComponent– ควบคมการ Update และ Draw ของมนยาก เพราะ

Game จะเรยกฟงกชนเหลานโดยอตโนมต

DrawableGameComponent

• ถาตองการเพม DrawableGameComponent เขาในเกม ใหสง Components.Add(<component ทตองการ>) ใน

constructor ของเกม

public Game1(){ graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; Components.Add(new MyComponent(this));}

• เมธอดตางๆ ของ DrawableGameComponent จะถกเรยก เมอเมธอดเดยวกนของ Game ถกเรยก

การใช GuiManager

• สรางฟลดชนด GuiManager ในคลาส Game• ใน constructor ของ Game– สราง GuiManager– เพม GuiManager เขาใน Components

การใช GuiManagerpublic class UserInterfaceDemoGame : Game {

public UserInterfaceDemoGame() { this.graphics = new GraphicsDeviceManager(this); this.gui = new GuiManager(this); Components.Add(this.gui); IsMouseVisible = true; }

// ...

private GuiManager gui;}

Screen

• GuiManager ตองม Screen ของมน– ชอซำากนกบ Screen ของ Game แตคนละตว

• Screen มหนาทจดการสถานะของ GUI ตางๆ• เราอาจม Screen หลายๆ อนถาเรามระบบ GUI หลายตว

• แตสวนมากจะมแคตวเดยว

Desktop Control

• ระบบ GUI จะมตนไมของ control– Control ตวหนงสามารถม control ตวอนเปนลก

• Control ทอยระดบบนสดเรยกวา Desktop Control– Control นเปนลกของ screen– Desktop Control เปนพนททไมมส– ถาคณเอา control อนไปใสเปนลกของ desktop

control มนจะปรากฏอยบนหนาจอเกมโดยตรง

การใช GuiManager

• ในฟงกชน Initialize ของ Game– สราง Screen โดยให Viewport ของ GraphicsDevice

เปน argument– ในขนนคณอาจจะกำาหนดขอบเขตของ Desktop

Control ไมใหใหญเกนไปได

การใช GuiManagerprotected override void Initialize() { Viewport viewport = GraphicsDevice.Viewport; Screen mainScreen = new Screen(viewport.Width, viewport.Height); this.gui.Screen = mainScreen;

mainScreen.Desktop.Bounds = new UniRectangle( new UniScalar(0.1f, 0.0f), new UniScalar(0.1f, 0.0f), // x and y new UniScalar(0.8f, 0.0f), new UniScalar(0.8f, 0.0f) // width and

height );

base.Initialize();}

UniScalar

• การบอกตำาแหนงของ Nuclex Framework ใชกลไกคลาย กบของ CeGUI (http://www.cegui.co.uk)

• Coordinate ทกตวแทนดวยคลาส UniScalar• UniScalar มสวนประกอบสองสวน– Fractional part = “ ”อตราสวนเทาไหรของความกวางของ พอ– Offset = หางจากตำาแหนงทกำาหนดวย fractional part มากพกเซล

• ตวอยาง– new UniScalar(0.5f, 50) = ตำาแหนงทหางจากจดกงกลางของ

พอมา 50 พกเซล

UniScalar

• ดงนนnew UniRectangle( new UniScalar(0.1f, 0.0f), new UniScalar(0.1f, 0.0f), // x and y new UniScalar(0.8f, 0.0f), new UniScalar(0.8f, 0.0f) // width and

height);

จงเปนสเหลยมทมขนาด 80% ของ control พอ และอยตรงกลาง control พอพอด

สราง Control อนๆ• สราง control อนๆ เพอมาใสเปนลกของ desktop

control• ในกรณนเราจะสราง WindowControl ซงเปน

หนาตางซงขางในสามารถบรรจ control อนๆ ได

สราง Control อนๆpublic partial class DemoDialog : WindowControl {

public DemoDialog() { InitializeComponent(); }

}

สราง Control อนๆpartial class DemoDialog {

private Nuclex.UserInterface.Controls.LabelControl helloWorldLabel; private Nuclex.UserInterface.Controls.Desktop.ButtonControl okButton; private Nuclex.UserInterface.Controls.Desktop.ButtonControl cancelButton; private void InitializeComponent() { this.helloWorldLabel = new Nuclex.UserInterface.Controls.LabelControl(); this.okButton = new Nuclex.UserInterface.Controls.Desktop.ButtonControl(); this.cancelButton = new

Nuclex.UserInterface.Controls.Desktop.ButtonControl();

// helloWorldLabel this.helloWorldLabel.Text = "Hello World! This is a label."; this.helloWorldLabel.Bounds = new UniRectangle(10.0f, 15.0f, 110.0f, 30.0f);

สราง Control อนๆ // okButton this.okButton.Bounds = new UniRectangle( new UniScalar(1.0f, -180.0f), new UniScalar(1.0f, -40.0f), 80, 24 ); // cancelButton this.cancelButton.Bounds = new UniRectangle( new UniScalar(1.0f, -90.0f), new UniScalar(1.0f, -40.0f), 80, 24 ); // DemoDialog this.Bounds = new UniRectangle(100.0f, 100.0f, 512.0f, 384.0f); Children.Add(this.helloWorldLabel); Children.Add(this.okButton); Children.Add(this.cancelButton); } }

Children.Add

• เมอสราง control เสรจแลวกใหไปเพมมนเปนลกของ control อนโดยการเรยกเมธอด Add ของ property Children

• เชน เราสามารถเพม DemoDialog เปนลกของ Desktop Control ไดดงตอไปน

protected override void Initialize() {

// ...code from previous step...

// Next, we add our demonstration dialog to the screen mainScreen.Desktop.Children.Add(new DemoDialog());

base.Initialize();}

การจดการกบ Event ของ Control ตางๆ

• เชนเดยวกบ WinForms controlcontrol ของ Nuclex Framework ม event

ทเราสามารถกำาหนด ใหมนได• เชน คลาส ButtonControl ม event Pressed ทจะถกสงทกครงเมอปมถกกด

• เราสามารถเพม handler ทม signature เปน

void Handler(object sender, EventArgs arguments)

ใหเปนตวจดการ event ได

การจดการกบ Event ของ Control ตางๆ

private void createDesktopControls(Screen mainScreen) {

// Button through which the user can quit the application ButtonControl quitButton = new ButtonControl(); quitButton.Text = "Quit"; quitButton.Bounds = new UniRectangle( new UniScalar(1.0f, -80.0f), new UniScalar(1.0f, -32.0f), 80, 32 ); quitButton.Pressed += delegate(object sender, EventArgs arguments) { Exit(); }; mainScreen.Desktop.Children.Add(quitButton);

}

Recommended