<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Videogames Laboratory</title>
	<atom:link href="http://videogameslab.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://videogameslab.wordpress.com</link>
	<description>O συναρπαστικός κόσμος της ανάπτυξης βιντεοπαιχνιδιών</description>
	<lastBuildDate>Mon, 08 Aug 2011 12:10:10 +0000</lastBuildDate>
	<language>el</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='videogameslab.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Videogames Laboratory</title>
		<link>http://videogameslab.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://videogameslab.wordpress.com/osd.xml" title="Videogames Laboratory" />
	<atom:link rel='hub' href='http://videogameslab.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Not for Sale! Τώρα και σε Windows Phone 7</title>
		<link>http://videogameslab.wordpress.com/2011/06/14/not-for-sale-windows-phone-7/</link>
		<comments>http://videogameslab.wordpress.com/2011/06/14/not-for-sale-windows-phone-7/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 10:27:40 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[Windows Phone 7]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=623</guid>
		<description><![CDATA[Το τελευταίο καιρό απόκτησα πρόσβαση σε ένα μηχάνημα με DirectX 11 κάρτα γραφικών. Οπότε δεν έχασα την ευκαιρία να δοκιμάσω λίγο Windows Phone 7 προγραμματισμό, κάνοντας μεταφορά εκεί το παιχνίδι Not for Sale! που αναπτύσσουμε στα πλαίσια του blog αυτού. Είναι αλήθεια ότι cross-platform ανάπτυξη βιντεοπαιχνιδιών με το XNA είχα δοκιμάσει και στο παρελθόν μεταξύ [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=623&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Το τελευταίο καιρό απόκτησα πρόσβαση σε ένα μηχάνημα με DirectX 11 κάρτα γραφικών. Οπότε δεν έχασα την ευκαιρία να δοκιμάσω λίγο Windows Phone 7 προγραμματισμό, κάνοντας μεταφορά εκεί το παιχνίδι Not for Sale! που αναπτύσσουμε στα πλαίσια του blog αυτού. Είναι αλήθεια ότι cross-platform ανάπτυξη βιντεοπαιχνιδιών με το XNA είχα δοκιμάσει και στο παρελθόν μεταξύ Windows και Xbox360, αλλά μια μεταφορά σε ένα smartphone έχει μεγαλύτερο ενδιαφέρον λόγω των ιδιαίτερων δυνατοτήτων και απαιτήσεων της πλατφόρμας. Το παιχνίδι το έχει κάνει ήδη <a title="Not for Sale! σε XNA 4.0" href="http://videogameslab.wordpress.com/2011/04/26/not-for-sale-xna-4/" target="_blank">μεταφορά σε XNA 4.0, για Windows</a>, o αναγνώστης του blog darklynx, οπότε ξεκινάμε με βάση αυτό.</span></p>
<p><span id="more-623"></span><span style="font-family:Verdana;font-size:9pt;">Μιας και ξεκινάμε με ένα υπάρχον project, το XNA μας παρέχει την δυνατότητα να δημιουργήσει με βάση αυτό ένα νέο project ειδικά για το WP7 (όπως και για το Xbox360 άλλωστε). Δεξί κλικ πάνω στο project <strong>NotForSale</strong>, επιλέγουμε <strong>Create Copy of Project for Windows Phone</strong>, και ξεκινάμε.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale1.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μετονομάζουμε το νέο project που θα δημιουργηθεί σε κάτι που να θυμίζει Windows Phone, πχ NotForSaleWP7. Παρατηρούμε ότι το νέο project περιέχει ήδη ένα σωρό αρχεία κώδικα. Αυτά τα αρχεία είναι στην ουσία τα ίδια αρχεία που περιέχει και το project NotForSale για Windows. Το «ίδια» το εννοούμε κυριολεκτικά. Δεν είναι αντίγραφα των αρχείων αυτών. Αν κάνουμε οποιαδήποτε αλλαγή σε κάποιο από τα αρχεία του νέου project (Windows Phone 7), αυτή θα επηρεάσει και το παλιό project (Windows). Έχουμε κοινή βάση κώδικα και για τα 2 project στην ουσία και αυτό είναι πολύ χρήσιμο καθώς δεν χρειάζεται να επαναλαμβάνουμε κώδικα. Επιπλέον παρατηρούμε ότι το project με το περιεχόμενο του παιχνιδιού (NotForSaleContent) είναι το ίδιο και για τις 2 εκδόσεις.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale2.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ήρθε η ώρα να δοκιμάσουμε την μαγεία του XNA λοιπόν, τρέχοντας το παιχνίδι στο WP7 emulator.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Βασικά όχι ακόμα. Το build αποτυγχάνει γιατί ο compiler δεν μπορεί να βρει τις αναφορές σε 2 βιβλιοθήκες που κάνουμε στο αρχείο Game1.cs<br />
</span></p>
<pre class="brush: csharp;">
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Μικρό το κακό, αυτές αφορούν επικοινωνία μέσω δικτύου και αποθήκευση αρχείων και μπαίνουν εξορισμού από το template που δημιουργεί τα αρχεία κώδικα. Μιας και δεν χρειαζόμαστε την λειτουργικότητα αυτή, μπορούμε να τα σβήσουμε χωρίς να επηρεάσουμε κανένα από τα 2 project. Δοκιμάζουμε ξανά:<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale3.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το παιχνίδι τρέχει αυτή τη φορά, αλλά φαίνεται λάθος. Στην ουσία όμως δεν είναι σφάλμα του XNA αυτό αλλά δικό μας. Σχεδιάζοντας το παιχνίδι για Windows αρχικά δεν λάβαμε υπόψη τις ιδιαιτερότητες του WP7. Στο constructor της κλάσης NotForSaleGame έχουμε θέσει την ανάλυση ως 1024&#215;768 κάτι που δεν μπορεί φυσικά να υποστηρίξει το WP7.<br />
</span></p>
<pre class="brush: csharp;">
        public NotForSaleGame ()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = “Content”;
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Λόγω της μεγάλης ανάλυσης (και μεγέθους) μιας οθόνης στο PC δεν σκεφτήκαμε καθόλου αν το παιχνίδι θα είναι portrait ή landscape, το αφήσαμε landscape, κάτι που δεν ταιριάζει καλά στο Window Phone 7 για το συγκεκριμένο είδος. Τα Space Shoot&#8217;em Up είναι συνήθως σε portrait (κατακόρυφα) μορφή, κληρονομία των παλιών arcade μηχανημάτων. Οπότε διορθώνουμε το orientation θέτοντας το property SupportedOrientation του αντικειμένου graphics σε DisplayOrientation.Portrait, αλλάζουμε την ανάλυση σε 480&#215;800 που είναι η native ανάλυση του Windows Phone 7 και ξαναδοκιμάζουμε.<br />
</span></p>
<pre class="brush: csharp;">
        public NotForSaleGame ()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = “Content”;
            graphics.PreferredBackBufferWidth = 480;
            graphics.PreferredBackBufferHeight = 800;

            graphics.SupportedOrientations = DisplayOrientation.Portrait;
        }
</pre>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale4.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Αυτή τη φορά τα πράγματα είναι καλύτερα, το παιχνίδι είναι σε portrait orientation, το φόντο είναι σωστό όπως και η προτροπή για να ξεκινήσει το παιχνίδι. Μόνο η υφή της Γης είναι λάθος, προφανώς γιατί ορίσαμε θέση και μέγεθος με απόλυτα νούμερα και όχι σε σχέση με το μέγεθος του viewport. Αυτό θα το φτιάξουμε παρακάτω, πρέπει όμως πρώτα να παρατηρήσουμε κάτι βασικό: Το παιχνίδι ακόμα περιμένει να πατήσουμε ENTER για να ξεκινήσει, δηλαδή περιμένει είσοδο από το πληκτρολόγιο, κάτι που φυσικά δεν γίνεται στο WP7. Όπως αποδείχθηκε, το ΧΝΑ δεν έχει πρόβλημα να υπάρχει κώδικας για Windows (πχ υποστήριξη για πληκτρολόγιο) σε παιχνίδι για WP7, απλά τον αγνοεί. Αυτό είναι καλό για μας γενικά (γιατί όπως είπαμε έχουμε κοινή βάση κώδικα και για τις 2 πλατφόρμες, χωρίς σφάλματα μη υποστήριξης) όμως υπάρχουν περιπτώσεις που επιθυμούμε διαχωρισμό του κώδικα και ειδική υποστήριξη για κάποια πλατφόρμα. Αυτό μπορεί να γίνει είτε για καλύτερη αναγνωσιμότητα είτε γιατί θέλουμε όντως διαφορετική λειτουργικότητα. Για παράδειγμα το μήνυμα &#8220;Press Enter to begin&#8221; δεν έχει νόημα στο WP7 θα ήθελα να μπορώ να απεικονίσω μια άλλη προτροπή πχ &#8220;Tap to begin&#8221;.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Για να υποστηρίξει αυτό το ΧΝΑ χρησιμοποιεί κάτι που ονομάζεται Conditional compilation (δεν είναι χαρακτηριστικό του XNA/C# υπάρχει και σε πολλές άλλες γλώσσες). Στην ουσία αυτό μοιάζει με το κλασσικό if-then-else statement και παίρνει την μορφή:<br />
</span></p>
<pre class="brush: csharp;">
#if WINDOWS_PHONE
//do something windows phone specific
#elif WINDOWS
//do something windows specific
#end
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η διαφορά του είναι ότι εκτελείται πριν το κυρίως compilation, σε ένα στάδιο preprocessing. Ανάλογα με την πλατφόρμα (WINDOWS_PHONE ή WINDOWS) ο compiler θα κρατήσει μόνο τα τμήματα κώδικα που αφορούν αυτή και θα αγνοήσει τα υπόλοιπα.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Για παράδειγμα, στην δική μας περίπτωση που θέλουμε διαφορετικά μηνύματα προτροπής για κάθε πλατφόρμα, το μόνο που έχουμε να κάνουμε είναι να χρησιμοποιήσουμε ένα conditional compilation στην μέθοδο Draw():<br />
</span></p>
<pre class="brush: csharp;">
            switch (gameState)
            {
                case GameState.Intro:
                    spriteBatch.Draw(logoTexture, new Rectangle(width / 5, 2* width / 5, 3 * width / 5, 3 * width / 5), Color.White);
#if WINDOWS_PHONE
                    renderStringCentered(arial, “Tap to begin”, height-100, Color.Yellow);
#elif WINDOWS
                    renderStringCentered(arial, “Press Enter to begin”,  height-100, Color.Yellow);
#endif
                    break;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στο τελικό εκτελέσιμο του παιχνιδιού, η γραμμή κώδικα που αφορά τα Windows δεν θα υπάρχει καθόλου, ο compiler θα κρατήσει μόνο αυτή που αφορά το WP7.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Πριν κλεισουμε την εισαγωγή στο conditional compilation, να δούμε που ορίζονται αυτά τα σύμβολα που χρησιμοποιήσαμε παραπάνω. Αν κάνουμε δεξί κλικ στο όνομα του project και ανοίξουμε τα Properties του, και στην συνέχεια το tab Build θα δούμε τα Conditional compilation symbols που στην περίπτωση του WP7 περιέχει το γνωστό WINDOWS_PHONE. Tο αντίστοιχο συμβαίνει στο project για τα Windοws. Μπορούμε αν θέλουμε να ορίσουμε και δικά μας σύμβολα και να τα χρησιμοποιήσουμε στο κώδικα με παρόμοιο τρόπο.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale5.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Στον παραπάνω κώδικα φτιάξαμε σιωπηλά το μέγεθος και την θέση της υφής της Γης, έτσι ώστε να ορίζεται σε συνάρτηση με το μέγεθος του viewport. Δοκιμάζουμε το παιχνίδι και πάλι:<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale6.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Δείχνει πολύ καλύτερο τώρα! Το μόνο πρόβλημα είναι ότι παρόλο το σωστό μήνυμα το παιχνίδι ακόμα περιμένει είσοδο από το πληκτρολόγιο για να ξεκινήσει:<br />
</span></p>
<pre class="brush: csharp;">
        protected override void Update(GameTime gameTime)
        {
            switch (gameState)
            {
                case GameState.Intro:
                    background.Update(gameTime);

                    if (keyClicked(Keys.Enter))
                    {
                        gameState = GameState.Playing;
                    }
                    else if (keyClicked(Keys.Escape))
                    {
                        this.Exit();
                    }

                    break;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στον παραπάνω κώδικα έχουμε «δέσει» την αλλαγή game state στην επιλογή συγκεκριμένων πλήκτρων του πληκτρολογίου. Κανονικά η όλη κατάσταση χρήζει μιας νέας κλάσης InputManager που να διαχειρίζεται ομοιόμορφα την είσοδο για κάθε πλατφόρμα αλλά μιας και θα ξεφύγουμε από το σκοπό μας, προς το παρόν θα δώσουμε μια πρόχειρη λύση. Αυτό που θα κάνουμε είναι θα αποσυνδέσουμε την αλλαγή game state από συγκεκριμένη είσοδο (είτε πληκτρολόγιο είτε touch), αντικαθιστώντας την μέθοδο keyClicked() με μια πιο αφηρημένη isInput() η οποία θα παίρνει ως όρισμα την επιλογή του χρήστη και θα επιστρέφει true αν όντως ο χρήστης έχει επιλέξει αυτή. Το μπέρδεψα λίγο, οπότε αφήνω το κώδικα μιλήσει με μεγαλύτερη γλαφυρότητα!<br />
</span></p>
<pre class="brush: csharp;">
            switch (gameState)
            {
                case GameState.Intro:
                    background.Update(gameTime);

                    if (isInput(InputOption.Start))
                    {
                        gameState = GameState.Playing;
                    }
                    else if (isInput(InputOption.Exit))
                    {
                        this.Exit();
                    }

                    break;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Είναι διαφωτιστική μια σύγκριση του νέου κώδικα με τον παλιό. Τώρα, αντί να ρωτάμε αν ο παίκτη πάτησε το πλήκτρο ENTER για να ξεκινήσει, ρωτάμε αν έχει επιλέξει ένα γενικό και αόριστο «Start». Αντί να ρωτάμε αν πάτησε το πλήκτρο ESC για να βγει από το παιχνίδι, ρωτάμε αν επέλεξε «Εxit». Οι επιλογές αυτές ορίζονται σε ένα enum στην αρχή της κλάσης NotForSaleGame.<br />
</span></p>
<pre class="brush: csharp;">
    Public enum InputOption
    {
        Start,
        Exit,
        Pause
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Έχοντας αποσυνδέσει την αλλαγή του game state από συγκεκριμένη συσκευή εισόδου ας περάσουμε στην υλοποίηση της μεθόδου isInput():<br />
</span></p>
<pre class="brush: csharp;">
        protected bool isInput(InputOption option)
        {
#if WINDOWS_PHONE
            if (gestureValid)
            {
                if (option == InputOption.Start &amp;&amp; gesture.GestureType == GestureType.Tap)
                {
                    return true;
                }
                else if (option == InputOption.Pause &amp;&amp; gesture.GestureType == GestureType.Hold)
                {
                    return true;
                }
                else if (option == InputOption.Exit &amp;&amp; gesture.GestureType == GestureType.DoubleTap)
                {
                    return true;
                }
            }
#elif WINDOWS
            if (option == InputOption.Start &amp;&amp; keyClicked(Keys.Enter))
            {
                return true;
            }
            else if (option == InputOption.Pause &amp;&amp; keyClicked(Keys.P))
            {
                return true;
            }
            else if (option == InputOption.Exit &amp;&amp; keyClicked(Keys.Escape))
            {
                return true;
            }
#endif
            return false;

        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Αξίζει να αναφέρουμε ότι θα μπορούσαμε να πετύχουμε το ίδιο αποτέλεσμα και χωρίς το conditional compilation στην συγκεκριμένη περίπτωση συνδυάζοντας πληκτρολόγιο και touch, πχ για την περίπτωση του &#8220;Start&#8221; θα κάναμε κάτι περίπου σαν και αυτό:</span></p>
<pre class="brush: csharp;">
        protected bool isInput(InputOption option)
        {
            if (option == InputOption.Start &amp;&amp; ( keyClicked(Keys.Enter) || gesture.GestureType == GestureType.Tap ))
            {
                return true;
            }
            return false;
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">To XNA όπως είπαμε θα αγνοήσει είσοδο που δεν υποστηρίζεται στην τρέχουσα πλατφόρμα. Χάριν απλότητας θα μείνουμε όμως στο conditional compilation προς το παρον.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ο κώδικας που αφορά Windows/πληκτρολογιο είναι λίγο-πολύ όπως και προηγουμένως, δηλαδή χρησιμοποιεί την keyClicked() σε συνδυασμό με την επιθυμητή επιλογή. Αυτό που διαφέρει αρκετά είναι ο κώδικας για το WP7. Στην συσκευή αυτή θα χρησιμοποιήσουμε διάδραση αφής στο παιχνίδι. Ενώ δεν είναι κάτι το πολύπλοκο, θέλει λίγη προσοχή. Το ΧΝΑ μας παρέχει την μέθοδο Touch.GetState(), που όπως και η αντίστοιχη του Keyboard, μας επιστρέφει την κατάσταση touch τη δεδομένη χρονική στιγμή. Το WP7 υποστηρίζει αρκετά πολύπλοκη touch διάδραση με 4 σημεία επαφής ταυτόχρονα και πολλά events για pressed, released, move καταστάσεις. Όλα αυτά περιέχονται TouchCollection αντικείμενο που επιστρέφει η Touch.GetState(), το οποίο μπορούμε να επεξεργαστούμε και να αλλάξουμε το game state ανάλογα. Για να μην μπούμε όμως σε βάθος στους μηχανισμούς αφής, στο συγκεκριμένο παράδειγμα θα χρησιμοποιήσουμε gestures, έτοιμες κινήσεις δηλαδή που αναγνωρίζει αυτόματα το XNA και μας τις επιστρέφει. Gestures για παράδειγμα είναι το tap, το διπλό tap, το drag, το hold κλπ. Εσωτερικά και τα gestures βασίζονται στην επεξεργασία του TouchCollection, και μπορούμε αν θέλουμε να τα υλοποιήσουμε (αυτά και άλλα) και από μόνοι μας.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Για να χρησιμοποιήσω κάποιο gesture πρέπει να το δηλώσω.<br />
</span></p>
<pre class="brush: csharp;">
        Protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            //declare which gestures we wish to use
            TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Hold | GestureType.DoubleTap | GestureType.FreeDrag;

            base.Initialize();
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στην Initialize() ορίζω ότι θα χρησιμοποιήσω τα gestures tap, hold, double tap και drag. Αν δεν κάνω αυτό το βήμα δεν θα μπορέσει το ΧΝΑ να αναγνωρίσει τα gestures αυτά.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Για να διαβάσω ένα gesture μπορώ να χρησιμοποιήσω τη μέθοδο TouchPanel.ReadGesture() η οποία επιστρέφει ένα αντικείμενο τύπου GestureSample. Για να διαβάσω ένα gesture με τη χρήση της ReadGesture() πρέπει πρώτα να βεβαιωθώ ότι υπάρχει κάποιο έτοιμο, ελέγχοντας την bool TouchPanel.IsGestureAvailable.<br />
</span></p>
<pre class="brush: csharp;">
            if (TouchPanel.IsGestureAvailable)
            {
                gesture = TouchPanel.ReadGesture();
            }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Έπειτα, ελέγχοντας το τύπο του gesture με τη χρήση της gesture.GestureType, μπορώ να αλλάξω το game state ανάλογα, κάτι που κάνουμε άλλωστε και στην isInput:<br />
</span></p>
<pre class="brush: csharp;">
        protected bool isInput(InputOption option)
        {
#if WINDOWS_PHONE
            if (gestureValid)
            {
                if (option == InputOption.Start &amp;&amp; gesture.GestureType == GestureType.Tap)
                {
                    return true;
                }
                else if (option == InputOption.Pause &amp;&amp; gesture.GestureType == GestureType.Hold)
                {
                    return true;
                }
                else if (option == InputOption.Exit &amp;&amp; gesture.GestureType == GestureType.DoubleTap)
                {
                    return true;
                }
            }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Τρέχουμε και πάλι το παιχνίδι, κάνουμε tap στην οθόνη και ναι, το παιχνίδι ξεκινά κανονικά! Λόγω του ότι είχαμε παραμετροποιήσει το σχεδιασμό της πίστας σε σχέση με το viewport οι εχθροί και τα εφέ εμφανίζονται κανονικά χωρίς την παραμικρή αλλαγή!<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale7.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Επίσης αν κάνουμε Hold στην οθόνη (πατήσουμε το δάκτυλο για περισσότερο από 1 δευτερόλεπτο) το παιχνίδι θα κάνει pause, και με double tap θα βγει στην αρχική οθόνη. Όλα καλα λοιπόν, το τελευταίο που μένει είναι να αλλάξουμε το κώδικα του PlayerShip έτσι ώστε να χρησιμοποιεί touch για την κίνηση και τους πυροβολισμούς. Θα χρησιμοποιήσουμε και πάλι conditional compilation για να διαχωρίσουμε το κώδικα WP7 και Windows.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Όσον αφορά τα gestures υπάρχει μια μικρή αλλά σημαντική λεπτομέρεια που δεν αναφέραμε ακόμα. Κάθε φορά που καλώ την ReadGesture για να λάβω ένα GestureSample αυτό αφαιρείται από το queue, και δεν μπορώ να ξανακαλέσω την ReadGesture για να το διαβάσω ξανά (η TouchPanel.IsGestureAvailable γίνεται αμέσως false). Οπότε αυτό που θα κάνουμε είναι να διαβάσουμε το gesture όταν αυτό γίνει διαθέσιμο μια φορά και θα το αποθηκεύσουμε σε μια μεταβλητή στη κλάση NotForSaleGame. Στην συνέχεια, σε οποιοδήποτε κομμάτι του κώδικα θέλουμε να ελέγξουμε το gesture θα ελέγχουμε την αποθηκευμένη μεταβλητή αντί να καλούμε την ReadGesture() ξανά. Αυτό το κάνουμε στην αρχή της Update:<br />
</span></p>
<pre class="brush: csharp;">
        protected override void Update(GameTime gameTime)
        {
            keyboardState = Keyboard.GetState();

            if (TouchPanel.IsGestureAvailable)
            {
                gesture = TouchPanel.ReadGesture();
                gestureValid = true;
            }
            else
            {
                gestureValid = false;
            }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Αυτή τη μεταβλητή gesture χρησιμοποιήσαμε και νωρίτερα στην isInput(), αυτή θα χρησιμοποιήσουμε και στο κώδικα για το PlayerShip για την κίνηση του παίκτη και τον πυροβολισμό:<br />
</span></p>
<pre class="brush: csharp;">
       public void Update(GameTime gameTime, Rectangle viewport)
        {
            base.Update(gameTime, viewport);

            timeToNext -= gameTime.ElapsedGameTime.TotalSeconds;

#if WINDOWS_PHONE
            if (game.GestureValid &amp;&amp; game.Gesture.GestureType == GestureType.FreeDrag)
            {
                //use displacement instead of exact position so as not to cover the playership with finger
                rectangle.X += (int) game.Gesture.Delta.X;
                rectangle.Y += (int) game.Gesture.Delta.Y;

                if (timeToNext                 {
                    //add a bullet on spacebar down
                    BulletManager.Manager.AddPlayerBullet(new Vector2(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2));

                    timeToNext = 0.2; // fire next bullet in 0.2 seconds
                }
            }

#elif WINDOWS
            if (game.KeyboardState.IsKeyDown(Keys.Left))
            {
                rectangle.X -= (int)(speed * gameTime.ElapsedGameTime.TotalSeconds);
            }
            else if (game.KeyboardState.IsKeyDown(Keys.Right))
            {
                rectangle.X += (int)(speed * gameTime.ElapsedGameTime.TotalSeconds);
            }

            if (game.KeyboardState.IsKeyDown(Keys.Up))
            {
                rectangle.Y -= (int)(speed * gameTime.ElapsedGameTime.TotalSeconds);
            }
            else if (game.KeyboardState.IsKeyDown(Keys.Down))
            {
                rectangle.Y += (int)(speed * gameTime.ElapsedGameTime.TotalSeconds);
            }

            if (game.KeyboardState.IsKeyDown(Keys.Space) &amp;&amp; timeToNext             {
                //add a bullet on spacebar down
                BulletManager.Manager.AddPlayerBullet(new Vector2(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2));

                timeToNext = 0.2; // fire next bullet in 0.2 seconds
            }
#endif
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Ο κώδικας για τα Windows δεν αλλάζει στην ουσία. Για το WP7 χρησιμοποιούμε το drag gesture που λειτουργεί διπλά, όσο κανω drag δηλαδή το δακτυλο πάνω στην οθόνη μετακινεί και το διαστημόπλοιο του παίκτη αλλά και παράλληλα πυροβολεί (autofire). Αν αφήσω την οθόνη το διαστημόπλοιο αλλά και το όπλο θα σταματήσουν. Χρησιμοποιώ την μετατόπιση του δακτύλου για να μετακινήσω το διαστημόπλοιο (Delta), αντί της απόλυτης X,Y θέσης του γιατί το βρίσκω περισσότερο εύχρηστο και μπορώ να μετακινώ το διαστημόπλοιο χωρίς να το καλύπτει το δάκτυλο μου (ή γενικά να εμποδίζει την ορατότητα άλλων αντικειμένων στο παιχνίδι).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Αυτό ήταν! Η μεταφορά του παιχνιδιού στο Windows Phone 7 είναι πλέον πλήρης! Από περιέργεια, να δούμε τι έγινε και με την έκδοση του παιχνιδιού μας για Windows, τρέχοντας το project για Windows:<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale8.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μια χαρά λειτουργεί και αυτό, με το orientation και την ανάλυση που ορίσαμε για το Windows Phone 7. Αν θέλουμε να ορίσουμε διαφορετικές τιμές για τα Windows είδαμε πως, μέσω conditional compilation.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τα συμπεράσματα από το όλο εγχείρημα είναι αρκετά. Παρόλο ίσως να μην φάνηκε, η μεταφορά του παιχνιδιού σε WP7 ήταν αρκετά απλή υπόθεση. Τις περισσότερες περιπτώσεις δεν χρειάζεται καμία αλλαγή στο κώδικα (αν και εμείς κάναμε μερικές για λόγους ευαναγνωσιμότητας κυρίως). Οι περισσότερες διαφορές εντοπίζονται στο input αναμενόμενα, με τη λογική και περιεχόμενο να παραμένουν τα ίδια. Το σημαντικότερο ίσως συμπέρασμα είναι ότι όταν σκοπεύουμε να αναπτύξουμε ένα cross-platform παιχνίδι, ιδιαίτερα για τόσο διαφορετικές πλατφόρμες, πρέπει να το σχεδιάσουμε εξαρχής. Το NotForSale! είχε σχεδιαστεί για Windows και αυτό φάνηκε αμέσως κατά την μεταφορά. Τέλος μια κλάση InputManager θα αυτοματοποιούσε την όλη διαδικασία ανάγνωσης εισόδου ανεξάρτητα πλατφόρμας και θα έκανε το κώδικα πιο ευανάγνωστο και καλύτερα οργανωμένο. Αυτό αφήνεται ως άσκηση για τον αναγνώστη όπως λένε! <img src='http://s2.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η όλη μεταφορά βασίστηκε στο WP7 Emulator λόγω έλλειψης συσκευής. Αν κάποιος έχει WP7 και θέλει να δοκιμάσει το παιχνίδι, θα ήταν χρήσιμο να μας πει τις εντυπώσεις του. Επίσης αν κάποιος θέλει να κάνει δωρεά μια συσκευή WP7 στο Τμήμα είναι καλοδεχούμενος! <img src='http://s2.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ο κώδικας του παιχνιδιού είναι διαθέσιμος σε <a href="http://code.google.com/p/videogameslab/downloads/detail?name=NotForSaleWP7.zip&amp;can=2&amp;q=" target="_blank">zip μορφή</a>.<br />
</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/623/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/623/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/623/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=623&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2011/06/14/not-for-sale-windows-phone-7/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale1.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale2.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale3.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale4.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale5.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale6.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale7.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2011/06/061411_1026_notforsale8.png" medium="image" />
	</item>
		<item>
		<title>Not for Sale! Κάτι σαν postmortem</title>
		<link>http://videogameslab.wordpress.com/2011/06/06/not-for-sale-postmortem/</link>
		<comments>http://videogameslab.wordpress.com/2011/06/06/not-for-sale-postmortem/#comments</comments>
		<pubDate>Mon, 06 Jun 2011 17:36:12 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=605</guid>
		<description><![CDATA[Πριν μερικές εβδομάδες προσκαλεσα ενδιαφερόμενα άτομα να συμμετάσχουν στην ανάπτυξη του Not for Sale!. Το είχα οραματιστεί ως μια συνεργατική ανάπτυξη στην οποία κάθε μέλος της ομάδας θα αναλάμβανε ένα υποσύστημα του παιχνιδιού. Το πλάνο ήταν να εξομοιώσουμε &#8220;πραγματικές&#8221; συνθήκες ανάπτυξης ενός βιντεοπαιχνιδιού, τουλάχιστον ως προς τα χρησιμοποιούμενα εργαλεία. Εκτοτε, επισκέφτηκαν το blog κάτι λιγότερο [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=605&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Πριν μερικές εβδομάδες προσκαλεσα ενδιαφερόμενα άτομα να <a title="Not for Sale! Το επόμενο βήμα" href="http://videogameslab.wordpress.com/2011/05/12/not-for-sale-the_next_step/" target="_blank">συμμετάσχουν στην ανάπτυξη του Not for Sale!</a>. Το είχα οραματιστεί ως μια συνεργατική ανάπτυξη στην οποία κάθε μέλος της ομάδας θα αναλάμβανε ένα υποσύστημα του παιχνιδιού. Το πλάνο ήταν να εξομοιώσουμε &#8220;πραγματικές&#8221; συνθήκες ανάπτυξης ενός βιντεοπαιχνιδιού, τουλάχιστον ως προς τα χρησιμοποιούμενα εργαλεία.</p>
<p>Εκτοτε, επισκέφτηκαν το blog κάτι λιγότερο από 2000 άτομα. Τα email συμμετοχών που έλαβα για την ανάπτυξη ήταν 4, εκ των οποίων τα 2 αναμενόμενα. Δηλαδή στην ουσία το 1 τοις χιλίοις των αναγνωστών του blog, που θα περίμενα ότι ενδιαφέρονται για την ανάπτυξη βιντεοπαιχνιδιών μιας και το διαβάζουν, ενδιαφέρθηκαν να συμμετάσχουν στην ανάπτυξη του Not for Sale!. Αυτό μου φάνηκε λίγο παράξενο για να είμαι ειλικρινής, οπότε για να καταλάβω τι συνέβει και να καθορίσω την τύχη ανάλογων προσπαθειών στο μέλλον, έφτιαξα το παρακάτω poll. Αν οι επιλογές δεν καλύπτουν κάποιον/α μπορεί ελεύθερα να εκφραστεί στα σχόλια.</p>
<a href="http://polldaddy.com/poll/5117648/">View This Poll</a>
<p>&nbsp;</p>
<p>Οπως φαίνεται, κατα πάσα πιθανότητα δεν θα συνεχιστεί η προσπάθεια αυτή με τη συνεργατική ανάπτυξη του παιχνιδιου. Θα ζητήσω απο άτομα που ενδιαφέρθηκαν, αν θέλουν, να ασχοληθούν στο δικό τους χρόνο με τα υποσυστήματα του παιχνιδιού της επιλογής τους και πιθανώς να γράψουν ένα μικρό tutorial.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/605/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/605/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/605/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=605&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2011/06/06/not-for-sale-postmortem/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>
	</item>
		<item>
		<title>Not for Sale! Το επόμενο βήμα</title>
		<link>http://videogameslab.wordpress.com/2011/05/12/not-for-sale-the_next_step/</link>
		<comments>http://videogameslab.wordpress.com/2011/05/12/not-for-sale-the_next_step/#comments</comments>
		<pubDate>Thu, 12 May 2011 06:21:38 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=599</guid>
		<description><![CDATA[Τους τελευταίους μήνες παρουσιάστηκε στο Videogames Laboratory blog μια σειρά από άρθρα πάνω στην ανάπτυξη ενός space shoot&#8217;em up με το όνομα Not for Sale!. Σκοπός του tutorial αυτού ήταν να δούμε περισσότερο σύνθετες τεχνικές ανάπτυξης βιντεοπαιχνιδιών, τεχνικές που ως ένα βαθμό χρησιμοποιούνται και σε εμπορικά παιχνίδια. Πιστεύω ότι φτάσαμε το παιχνίδι σε ένα καλό [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=599&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Τους τελευταίους μήνες παρουσιάστηκε στο Videogames Laboratory blog <a href="http://el.wordpress.com/tag/not-for-sale/" target="_blank">μια σειρά από άρθρα</a> πάνω στην ανάπτυξη ενός space shoot&#8217;em up με το όνομα Not for Sale!. Σκοπός του tutorial αυτού ήταν να δούμε περισσότερο σύνθετες τεχνικές ανάπτυξης βιντεοπαιχνιδιών, τεχνικές που ως ένα βαθμό χρησιμοποιούνται και σε <a href="http://spacedebristhegame.blogspot.com/" target="_blank">εμπορικά παιχνίδια</a>. Πιστεύω ότι φτάσαμε το παιχνίδι σε ένα καλό επίπεδο με διαχείριση εχθρών, ανίχνευση συγκρούσεων, επεκτάσιμο σύστημα εφέ και εκρήξεων. Μένουν όμως πολλά να γίνουν ακόμα πριν να μπορέσουμε να το χαρακτηρίσουμε ολοκληρωμένο παιχνίδι.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μια και ο σκοπός του blog αυτού είναι πρωτίστως εκπαιδευτικός, και μιας και δεν υπάρχει καλύτερος τρόπος να μάθεις από το να εμπλακείς ο ίδιος στην μάθηση, πιστεύω ότι ήρθε η ώρα να εμπλέξουμε τους αναγνώστες στην κατασκευή του παιχνιδιού. Θέλω λοιπόν να οργανώσουμε μια συνεργατική ανάπτυξη του παιχνιδιού στην οποία μπορεί να συμμετάσχει οποιοσδήποτε αναγνώστης του blog επιθυμεί και πιστεύει ότι μπορεί να συνεισφέρει.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η ανάπτυξη θα οργανωθεί σε πρότυπα εταιριών ανάπτυξης, χρησιμοποιώντας όσο γίνεται ανάλογα εργαλεία. Για τον διαμοιρασμό κώδικα θα χρησιμοποιήσουμε το Google Code (svn) που ήδη χρησιμοποιούμε για τα tutorial. Για καταγραφή και διαχείριση σφαλμάτων θα χρησιμοποιήσουμε κάποιο bug tracking εργαλείο. Τα μέλη της ομάδας θα κρατάνε ημερολόγιο ανάπτυξης με την πρόοδο τους. Για την ανάπτυξη καθαυτή θα χρησιμοποιήσουμε το XNA Game Studio 4.0. Μόνο αναψυκτικά και φρούτα ή σοκολάτες δεν θα προσφέρουμε!<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Επιπλέον, όπως και στις μεγάλες ομάδες ανάπτυξης, κάθε μέλος της ομάδας θα είναι υπεύθυνο και θα του «ανήκει» ένα υποσύστημα του παιχνιδιού. Για παράδειγμα αν κάποιος είναι υπεύθυνος για το σύστημα εφέ (με particle systems) μόνο εκείνος θα μπορεί να διορθώνει bugs και να το επεκτείνει.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Με μια πρώτη καταγραφή διακρίνω τα παρακάτω υποσυστήματα στο παιχνίδι:<br />
</span></p>
<ul>
<li><span style="font-family:Verdana;font-size:9pt;">Σύστημα εφέ. Θα περιλαμβάνει τα πάντα από εκρήξεις μέχρι οποιοδήποτε άλλο εφέ (βροχή μετεωριτών, λάμψη από πυροβολισμούς κλπ).<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Audio manager για αναπαραγωγή εφέ και μουσικής.<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Σύστημα tile map για την τοποθέτηση του κυλιόμενου φόντου. Θα χρησιμοποιηθεί κανονικό tile map και όχι το απλοϊκό που χρησιμοποιείται τώρα. Προτείνεται η χρήση του Tiled Editor ο οποίος υποστηρίζεται από το XNA.<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Bonus system που θα αναβαθμίζει το παίκτη ανάλογα (είτε σε όπλα, είτε σε ασπίδα, ζωή κλπ)<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Animation συστημα για τους εχθρούς έτσι ώστε να κινούνται σε «κύματα» και να έχουν διακριτές κινήσεις<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Αναβαθμίσιμο weapon system για το παίκτη<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Level editor για το σχεδιασμό της πίστας. Αυτό μπορεί να βασίζεται στο <a href="http://www.mapeditor.org/" target="_blank">Tiled Editor</a> ή μπορεί να είναι ξεχωριστό εργαλείο.<br />
</span></li>
<li><span style="font-family:Verdana;font-size:9pt;">Σύστημα για την φόρτωση και παραμετροποίηση περιεχομένου μέσω xml αρχείων. Αυτό θα συνδέεται με το Level Editor.<br />
</span></li>
</ul>
<p><span style="font-family:Verdana;font-size:9pt;">Ότι λείπει μπορούμε να το συμπληρώσουμε στην πορεία.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Όσον αφορά τα γραφικά του παιχνιδιού μπορούμε να χρησιμοποιήσουμε τα υπάρχοντα. Ακόμα καλύτερα αν κάποιος τα καταφέρνει σε pixel art και θέλει να συνεισφέρει ακόμα καλύτερα. Το ίδιο ισχύει και για τον ήχο/μουσική. Επίσης θα χρειαστεί και ο ρόλος του Level Designer, ένα άτομο που θα τοποθετήσει τους εχθρούς σε συγκριμένα σημεία στη πίστα και θα ορίσει Hit Points, όπλα παίκτη και τα bonus που θα εμφανίζονται και πως αυτά θα αναβαθμίζουν το παίκτη. Αυτά θα ορίζονται όλα μέσω του Level Editor.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το παιχνίδι θα αναπτυχθεί σε εκπαιδευτικούς σκοπούς όπως ανέφερα, και όχι για εμπορικούς, κανένα από τα μέλη της ομάδας (ούτε και εγώ φυσικά) θα μπορούμε να το εκμεταλλευτούμε ανάλογα. Ο κώδικας, εργαλεία και το περιεχόμενο θα είναι διαθέσιμος σε όλους. Το παιχνίδι θα βασιστεί στον <a href="http://videogameslab.wordpress.com/2011/04/26/not-for-sale-xna-4/" target="_blank">υπάρχοντα κώδικα</a> του Not For Sale! αλλά το κάθε μέλος μπορεί να αλλάξει, βελτιώσει και να αναβαθμίσει το αντίστοιχο υποσύστημα όπως νομίζει.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Οπότε για να ολοκληρώσω το κάλεσμα, όποια/όποιος ενδιαφέρεται να συμμετάσχει στην ανάπτυξη ας επικοινωνήσει μαζί μου μέσω του <a href="http://videogameslab.wordpress.com/contact/" target="_blank">contact form</a> με τα στοιχεία της/του και το υποσύστημα/τομέα με τον οποίο θα ήθελε να ασχοληθεί. Όλοι είναι ευπρόσδεκτοι με έναν όρο: <strong>όποιος συμφωνήσει να συμμετάσχει στην ανάπτυξη παρακαλώ να το εννοεί</strong>. Είναι πολύ άσχημο, και για τα υπόλοιπα μέλη, να ξεκινήσει κάποιος την ανάπτυξη και μετά να τα παρατήσει ή να μην διορθώνει bugs που εμποδίζουν την πρόοδο των υπόλοιπων κλπ. Αυτή είναι και η έννοια της «ιδιοκτησίας» ενός υποσυστήματος. Επίσης καλό θα ήταν αυτός που θα εμπλακεί με την ανάπτυξη να έχει κάποιο επίπεδο γνώσης προγραμματισμού (αντικειμενοστραφούς). Δεν είναι απαραίτητη η γνώση του ΧΝΑ ή της C# αλλά το πως ορίζονται οι κλάσεις, πως δουλεύει η κληρονομικότητα κλπ πρέπει να τα κατέχει.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Σχετικά με το δικό μου ρόλο τώρα, εγώ δεν θα εμπλακώ άμεσα με την ανάπτυξη (δηλαδή τη συγγραφή κώδικα). Κάνω πίσω σε αυτή την περίπτωση και δίνω σε εσάς το λόγο. Ο δικός μου ρόλος θα είναι περισσότερο οργανωτικός, συμβουλευτικός, ρόλος «μέντορα» αν θέλετε.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ας κάνουμε λοιπόν αυτή τη προσπάθεια, αυτό να πείραμα να δούμε αν μπορούμε να τα καταφέρουμε σε μια συνεργατική ανάπτυξη ενός βιντεοπαιχνιδιού. Πιστεύω ότι έχουμε όλοι πολλά να μάθουμε από αυτό.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;"><a href="http://videogameslab.wordpress.com/contact/" target="_blank">Περιμένω τις συμμετοχές σας</a>.</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/599/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/599/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/599/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=599&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2011/05/12/not-for-sale-the_next_step/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>
	</item>
		<item>
		<title>Not for Sale! σε XNA 4.0</title>
		<link>http://videogameslab.wordpress.com/2011/04/26/not-for-sale-xna-4/</link>
		<comments>http://videogameslab.wordpress.com/2011/04/26/not-for-sale-xna-4/#comments</comments>
		<pubDate>Tue, 26 Apr 2011 09:01:14 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=595</guid>
		<description><![CDATA[Είχα εδώ και καιρό υποσχεθεί ανακοινώσεις σχετικά με την ανάπτυξη του Not for Sale! οι οποίες ακόμα έρχονται. Ελπίζω μέσα στις επόμενες μέρες να καταφέρω να ανεβάσω το σχετικό post. Κάτι σχετικό, ο αναγνώστης του Videogames Laboratory darklynx είχε την ευγενή καλοσύνη να μετατρέψει το project του παιχνιδιού σε XNA 4.0 και να το ανεβάσει [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=595&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Είχα εδώ και καιρό υποσχεθεί ανακοινώσεις σχετικά με την ανάπτυξη του Not for Sale! οι οποίες ακόμα έρχονται. Ελπίζω μέσα στις επόμενες μέρες να καταφέρω να ανεβάσω το σχετικό post.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Κάτι σχετικό, ο αναγνώστης του Videogames Laboratory darklynx είχε την ευγενή καλοσύνη να μετατρέψει το project του παιχνιδιού σε XNA 4.0 και να το ανεβάσει στο <a href="http://code.google.com/p/videogameslab/source/checkout" target="_blank">Code Repository</a>. Τον ευχαριστώ πολύ για την προσπάθεια του αυτή. Οδηγίες για τη χρήση του Code Repository μπορείτε να βρείτε <a href="http://videogameslab.wordpress.com/2009/07/22/code-repository/" target="_blank">εδώ</a> και <a href="http://videogameslab.wordpress.com/2009/07/23/code-repository-2/" target="_blank">εδώ</a>.<br />
</span></p>
<p><span style="font-size:9pt;"><span style="font-family:Verdana;">Εν αναμονή των ανακοινώσεων λοιπόν, καλό game coding! </span><span style="font-family:Verdana;"><br />
</span></span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/595/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/595/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/595/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=595&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2011/04/26/not-for-sale-xna-4/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>
	</item>
		<item>
		<title>Not for Sale! Εκρήξεις και particle systems</title>
		<link>http://videogameslab.wordpress.com/2011/03/04/not-for-sale-particle-systems/</link>
		<comments>http://videogameslab.wordpress.com/2011/03/04/not-for-sale-particle-systems/#comments</comments>
		<pubDate>Fri, 04 Mar 2011 08:22:26 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=585</guid>
		<description><![CDATA[Έχει περάσει πολύς καιρός από το προηγούμενο άρθρο στη σειρά tutorial ανάπτυξης του Not for Sale! Και ενώ ο κώδικας για το συγκεκριμένο άρθρο είναι έτοιμος εδώ και καιρό, δεν μπορούσα να βρω χρόνο για να γράψω το κείμενο. Ούτε τώρα βρήκα, αλλά για να μην κάθεται ο κώδικας τον ανεβάζω με μερικά σύντομα σχόλια [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=585&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Έχει περάσει πολύς καιρός από το προηγούμενο άρθρο στη σειρά tutorial ανάπτυξης του Not for Sale! Και ενώ ο κώδικας για το συγκεκριμένο άρθρο είναι έτοιμος εδώ και καιρό, δεν μπορούσα να βρω χρόνο για να γράψω το κείμενο. Ούτε τώρα βρήκα, αλλά για να μην κάθεται ο κώδικας τον ανεβάζω με μερικά σύντομα σχόλια για την λειτουργία του.<span id="more-585"></span><br />
Σήμερα θα ασχοληθούμε με τα συστήματα σωματιδίων (particle systems) τα οποία είναι ένας εύκολος και ευέλικτος τρόπος να δημιουργήσουμε πολλών διαφορετικών ειδών εφέ σε ένα παιχνίδι. Είχα κάνει μια σύντομη αναφορά στα συστήματα σωματιδίων σε ένα <a title="Εισαγωγή στην OpenGL: μέρος 6o (πραγματικά τελευταίο)" href="http://videogameslab.wordpress.com/2009/01/28/opengl-intro6/" target="_blank">παλιό άρθρο για την OpenGL</a>, η λογική στην οποία θα βασιστούμε είναι ίδια και στην συγκεκριμένη περίπτωση.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Σωματίδιο (particle) ονομάζουμε μια «οντότητα» που χαρακτηρίζεται από διάφορες παραμέτρους, κατ&#8217;ελάχιστον μια θέση στο χώρο, το χρόνο ζωής και ένα διάνυσμα ταχύτητας. Σε ένα σωματίδιο μπορούμε να αποδώσουμε φυσικές παραμέτρους όπως βαρύτητα, επιβράδυνση, μέγεθος, διαφάνεια. Επιπλέον μπορούμε να απεικονίσουμε μια υφή σε αυτό και να το φωτίσουμε με τα φώτα της σκηνής. Αν θεωρήσουμε έναν πεπερασμένο αριθμό τέτοιων σωματιδίων και μια πηγή που τα εκπέμπει τότε έχουμε ένα σύστημα σωματιδίων (particle system).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Στα πλαίσια του παιχνιδιού θα αναπαραστήσουμε ένα σωματίδιο με μια class Particle που θα περιέχει τα παρακάτω δεδομένα<br />
</span></p>
<pre class="brush: csharp;">
    class Particle
    {
        Rectangle rectangle;
        float rotationSpeed;
        float rotation;
        Vector2 velocity;
        float timeToLive;
        float elapsedTimeToLive;
        float startSize;
        float endSize;
        bool active;
        Vector2 origin;
        Texture2D texture;
        Color color;
     }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στην ουσία ορίζουμε το Rectangle της υφής του σωματιδίου (που καθορίζει θέση και μέγεθος), μια υφή Texture2D, ταχύτητα Velocity, περιστροφή Rotation, χρόνο ζωής timeToLive κλπ. Επιπλέον ορίζουμε και τις μεθόδους Reset, Update και Render που καθορίζουν την συμπεριφορά του κάθε particle:<br />
</span></p>
<pre class="brush: csharp;">
   class Particle
    {
        //data

        public bool Active
        {
            get { return active; }
        }

        public Particle(Texture2D texture)
        {
            rectangle = new Rectangle(0, 0, 1, 1);
            this.texture = texture;
            active = false;

            //set origin to sprite centre for correct rotation
            origin = new Vector2(texture.Width / 2, texture.Height / 2);
        }

        public void Reset(Vector2 position, Vector2 velocity, float startSize, float endSize, float rotationSpeed, Color color, float timeToLive)
        {
            this.timeToLive = timeToLive;
            this.rotationSpeed = rotationSpeed;
            this.velocity = velocity;
            this.startSize = startSize;
            this.endSize = endSize;
            this.color = color;

            //set position and dimensions
            rectangle.X = (int)position.X;
            rectangle.Y = (int)position.Y;

            active = true;
            rotation = 0;

            elapsedTimeToLive = timeToLive;
        }

        public void Update(GameTime gameTime, Rectangle viewport)
        {
            if (active)
            {
                float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
                elapsedTimeToLive -= elapsed;

                //move particle
                rectangle.X += (int)(velocity.X * elapsed);
                rectangle.Y += (int)(velocity.Y * elapsed);

                float size = MathHelper.Lerp(startSize, endSize, 1 - elapsedTimeToLive / timeToLive);
                rectangle.Width = (int)size;
                rectangle.Height = (int)size;

                //increase rotation
                rotation += rotationSpeed * elapsed;

                //reduce particle opacity as time passes by. It will reach 0 when timeToLive reaches 0.
                color.A = (byte)(255 * elapsedTimeToLive / timeToLive);

                //if time to live reaches 0 or particle goes out of viewport then particle dies
                if (elapsedTimeToLive &lt;= 0 || !rectangle.Intersects(viewport))
                {
                    active = false;
                }
            }
        }

        public void Render(SpriteBatch sb)
        {
            if (active)
            {
                //if active, draw particle
                sb.Draw(texture, rectangle, null, color, rotation, origin, SpriteEffects.None, 0);
            }
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">H Reset είναι βασικής σημασίας μιας και σε ένα σύστημα σωματιδίων ανακυκλώνουμε σωματίδια και δεν δημιουργούμε συνεχώς νέα, για λόγους απόδοσης. Είναι σημαντικό λοιπόν να μπορούμε να επαναρχικοποιούμε ένα &#8220;dead&#8221; σωματίδιο. Κατά τα άλλα η Update αλλάζει τις παραμέτρους ενός σωματιδίου (θέση, περιστροφή, διαφάνεια κλπ) όσο αυτό είναι ενεργό (elapsedTimeToLive &lt;= 0). Την διαφάνεια του σωματιδίου την ελαττώνουμε γραμμικά σε σχέση με το χρόνο έτσι ώστε αυτό θα εξαφανίζεται προς το τέλος της ζωής του ομαλά: color.A = (byte)(255 * elapsedTimeToLive / timeToLive);<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η Render απλά απεικονίζει το sprite ενός ενεργού σωματιδίου. Εδώ χρησιμοποιούμε μια διαφορετική έκδοση της Draw η οποία δέχεται ως όρισμα την περιστροφή rotation. Προσοχή πρέπει να δοθεί επίσης στην παράμετρο origin η οποία ορίζει το σημείο περιστροφής (pivot), το οποίο έχουμε ορίζει ως το κέντρο του sprite.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τώρα, μιας και τα σωματίδια από μόνα τους δεν κάνουν και πολλά, πρέπει να ορίσουμε και ένα σύστημα το οποίο θα τα διαχειρίζεται. Αυτό το κάνει η κλάση ParticleSystem:<br />
</span></p>
<pre class="brush: csharp;">
    abstract class ParticleSystem : GameEntity
    {
       //particle system parameters
        protected List particles;
        protected int maxParticles;
        protected bool active;
        protected bool emitParticles;
        protected float timeToNext;
        protected Random random;
        protected Vector2 position;
        protected Vector2 velocity;
        protected float duration;
        protected float emitParticleDuration;
        protected float rate;
        protected bool alwaysActive;

        //particle parameters
        protected float minHorizontalSpeed, maxHorizontalSpeed;
        protected float minVerticalSpeed, maxVerticalSpeed;
        protected float minRotationSpeed, maxRotationSpeed;
        protected float minTimeToLive, maxTimeToLive;
        protected int minStartSize, maxStartSize;
        protected int minEndSize, maxEndSize;
        protected Vector2 gravity;
        protected Color minColor, maxColor;

        //a particle system is considered active while at least one of the emitted particles is still alive
        public bool Active
        {
            get { return active; }
        }

        public Vector2 Velocity
        {
            set { velocity = value; }
        }

        public Vector2 Position
        {
            set { position = value; }
        }

        public bool EmitParticles
        {
            set { emitParticles = value; }
        }

        public ParticleSystem()
        { }

        public ParticleSystem(Texture2D texture)
        {
            this.texture = texture;
        }

        protected virtual void InitialiseSystem()
        {
        }

        public void Initialise()
        {
            alwaysActive = false;
            duration = 0f;

            InitialiseSystem();

            if (!alwaysActive &amp;&amp; duration &gt; 0f)
            {
                rate = maxParticles / duration;
            }

            particles = new List(maxParticles);
            for (int i = 0; i &lt; maxParticles; i++)
            {
                particles.Add(new Particle(texture));
            }

            active = false;
            emitParticles = false;

            random = new Random();
        }

        public virtual void Reset(Vector2 position, Vector2 velocity)
        {
            active = true;
            emitParticles = true;
            this.position = position;
            this.velocity = velocity;

            emitParticleDuration = duration;
        }

        public override void Update(GameTime gameTime, Rectangle viewport)
        {
            //if particle system is active, update it
            if (active)
            {
                timeToNext -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                emitParticleDuration -= (float)gameTime.ElapsedGameTime.TotalSeconds;

                //check if we still emit particles
                if (emitParticles &amp;&amp; timeToNext &lt;= 0)
                {
                    //time to generate a new particle. Find the first unused in the list
                    foreach (Particle particle in particles)
                    {
                        if (!particle.Active)
                        {
                            //find particle velocity based on specified horizontal-vertical range
                            Vector2 vel = new Vector2(MathHelper.Lerp(minHorizontalSpeed, maxHorizontalSpeed, (float)random.NextDouble()),
                                                      MathHelper.Lerp(minVerticalSpeed, maxVerticalSpeed, (float)random.NextDouble()));

                            //add random velocity to particle system velocity to provide a uniform direction to all particles
                            vel += velocity;

                            //add gravity to the velocity
                            vel += gravity;

                            //find particle start size;
                            float startSize = MathHelper.Lerp(minStartSize, maxStartSize, (float)random.NextDouble());

                            //find particle end size
                            float endSize = MathHelper.Lerp(minEndSize, maxEndSize, (float)random.NextDouble());

                            //find particle rotation speed
                            float rotationSpeed = MathHelper.Lerp(minRotationSpeed, maxRotationSpeed, (float)random.NextDouble());

                            //how long will the particle live?
                            float timeToLive = MathHelper.Lerp(minTimeToLive, maxTimeToLive, (float)random.NextDouble());

                            //determine particle color
                            Color color = Color.Lerp(minColor, maxColor, (float)random.NextDouble());

                            //reset particle
                            particle.Reset(position, vel, startSize, endSize, rotationSpeed, color, timeToLive);

                            break;
                        }
                    }
                    timeToNext = 1.0f / rate;
                }

                if (emitParticleDuration &lt;= 0 &amp;&amp; !alwaysActive)
                {
                    //stop emitting particles
                    emitParticles = false;
                }

                active = false;

                //update particles
                foreach (Particle particle in particles)
                {
                    if (particle.Active)
                    {
                        particle.Update(gameTime, viewport);
                    }

                    //particle system is active as long as at least one particle is active
                    //this check is not valid if the particle system is always active
                    active = active || particle.Active || alwaysActive;
                }
            }
        }

        public override void Render(SpriteBatch sb)
        {
            foreach (Particle particle in particles)
            {
                //render all active particles
                if (particle.Active)
                {
                    particle.Render(sb);
                }
            }
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Ένα particle system είναι στην ουσία μια πηγή η οποία «γεννά» νέα σωματίδια με συγκεκριμένο ρυθμό, ταχύτητα και διεύθυνση. Τη θεωρούμε μια «οντότητα» όπως κάθε άλλο game entity του παιχνιδιού η οποία έχει χρόνο ζωής και μπορεί να μετακινηθεί αν χρειαστεί και η ίδια (φανταστείτε τη φωτιά που βγάζει η τουρμπίνα ενός αεροσκάφους για παράδειγμα). Στην υλοποίηση μας κάνουμε την κλάση ParticleSystem να «κληρονομεί» την GameEntity περισσότερο για ευκολία μιας και έτσι μπορούμε να χρησιμοποιήσουμε το Game Entity Factory του παιχνιδιού και να δημιουργούμε νέα particle systems προσδιορίζοντας το όνομα τους και μόνο.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση ParticleSystem κρατά 2 σετ παραμέτρους, αυτές που αφορούν το particle system ίδιο:<br />
</span></p>
<pre class="brush: csharp;">
      //particle system parameters
        protected List particles;
        protected int maxParticles;
        protected bool active;
        protected bool emitParticles;
        protected float timeToNext;
        protected Random random;
        protected Vector2 position;
        protected Vector2 velocity;
        protected float duration;
        protected float emitParticleDuration;
        protected float rate;
        protected bool alwaysActive;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">και αυτές που αφορούν τα σωματίδια τα οποία «γεννά»:<br />
</span></p>
<pre class="brush: csharp;">
        //particle parameters
        protected float minHorizontalSpeed, maxHorizontalSpeed;
        protected float minVerticalSpeed, maxVerticalSpeed;
        protected float minRotationSpeed, maxRotationSpeed;
        protected float minTimeToLive, maxTimeToLive;
        protected int minStartSize, maxStartSize;
        protected int minEndSize, maxEndSize;
        protected Vector2 gravity;
        protected Color minColor, maxColor;
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το κάθε particle system διατηρεί μια λίστα (List particles) με ένα maximum αριθμό σωματιδίων τα οποία δημιουργεί κατά την δημιουργία του και επαναχρησιμοποιεί κάθε φορά που το ενεργοποιούμε. Δεν αποδεσμεύουμε ποτέ τα σωματίδια όσο τρέχει το game loop και δεν δημιουργούμε νέα. Αυτό είναι ιδιαίτερα σημαντικό σε managed περιβάλλοντα όπως το .NET με αυτόματη διαδικασία Garbage Collection (αλλά και εκτός managed περιβαλλόντων δεν θεωρείται καλή πρακτική γενικά να δεσμεύουμε/αποδεσμεύουμε μνήμη στο game loop). Επιπλέον το particle system έχει πληροφορίες όπως θέση και ταχύτητα της πηγής, ρυθμός με τον οποίο γεννά σωματίδια κλπ.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Όσον αφορά τα σωματίδια τα ίδια, κάθε σωματίδιο που γεννά η πηγή πρέπει να αρχικοποιηθεί με κάποιες παραμέτρους όπως η ταχύτητα του, η περιστροφή του, το μέγεθος του, το χρώμα του κλπ. Για να δώσουμε την αίσθηση του τυχαίου και της ποικιλίας στο σωματίδια που δημιουργεί η πηγή δεν ορίζουμε απόλυτες τιμές για τις παραμέτρους αυτές (διαφορετικά θα ήταν όλα ίδια τα σωματίδια και το εφφε όχι τόσο εντυπωσιακό), αλλά ορίζουμε εύρος τιμής για τη κάθε μια παράμετρο. Παράδειγμα για την οριζόντια ταχύτητα ορίζουμε το εύρος [minHorizontalSpeed, maxHorizontalSpeed], για το αρχικό μέγεθος [minStartSize, maxStartSize], για το χρώμα [minColor, maxColor]. Το particle system κάθε φορά που δημιουργεί (δηλαδή επαναρχικοποιεί) ένα σωματίδιο διαλέγει τυχαία μια τιμή για κάθε παράμετρο από αυτό το εύρος τιμών.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Από τις μεθόδους της κλάσης ParticleSystem θα σταθώ σε 2: Η Update αν και φαίνεται περίπλοκη αυτό που κάνει είναι, όσο το σύστημα είναι ενεργό, να εκπέμπει νέα σωματίδια (επιλέγοντας τα ανενεργά από τη λίστα particles) αρχικοποιώντας τα με τυχαίες τιμές από το εύρος τιμών για κάθε παράμετρο, όπως εξηγήσαμε παραπάνω. Πρέπει να παρατηρήσουμε ότι χρησιμοποιούμε 2 boolean μεταβλητές για να ελέγξουμε τη λειτουργία του συστήματος την active και την emitParticles. Όταν η emitParticles είναι false το σύστημα σταματά να γεννά νέα σωματίδια, δεν παύει όμως να είναι ενεργό (active). Το σύστημα γίνεται ανενεργό (οπότε σταματάμε να το κάνουμε Update και Render) μόνο όταν έχουν «πεθάνει» όλα τα σωματίδια που έχει δημιουργήσει. Σε διαφορετική περίπτωση θα δούμε τα σωματίδια να σβήνουν απότομα στην οθόνη όταν απενεργοποιηθεί το σύστημα σωματιδίων. Ενδιαφέρον έχει επίσης και η μέθοδος Initialise() η οποία δημιουργεί τη λίστα με τα σωματίδια (particles). Επιπλέον καλή και μια virtual μέθοδο με το όνομα InitialiseSystem που στην κλάση ParticleSystem δεν φαίνεται να κάνει τίποτα. Και με αφορμή αυτό να εξηγήσω γιατί η κλάση ParticleSystem είναι abstract.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ο λόγος που η ParticleSystem είναι abstract είναι γιατί δεν θέλω ποτέ να δημιουργήσω ένα αντικείμενο από αυτή. Μιας και δεν έχω δώσει τιμές στις παραμέτρους του particle system αυτό δεν θα κάνει τίποτα. Αυτό που θέλω είναι να ενσωματώσω όλα τα δεδομένα και τις βασικές λειτουργίες ενός συστήματος στην κλάση ParticleSystem και με την χρήση subclassing να δημιουργήσω νέες κλάσης που δίνουν τιμές στις παραμέτρους και εξειδικεύουν την λειτουργία του συστήματος κάνοντας override InitialiseSystem. Αυτό μου δίνει την δυνατότητα να δημιουργήσω μια μεγάλη ποικιλία εφφέ. Για παράδειγμα, η παρακάτω κλάση δημιουργεί μια έκρηξη:<br />
</span></p>
<pre class="brush: csharp;">
    class ParticleExplosion : ParticleSystem
    {
        public ParticleExplosion(Texture2D texture) : base(texture)
        {
        }

        protected override void InitialiseSystem()
        {
            //set params specific to the explosion particle system
            maxParticles = 100;
            duration = 0.5f;

            minHorizontalSpeed = -100;
            maxHorizontalSpeed = 100;
            minVerticalSpeed = -100;
            maxVerticalSpeed = 100;

            minRotationSpeed = -3;
            maxRotationSpeed = 3;
            minTimeToLive = 0.5f;
            maxTimeToLive = 1.0f;

            minStartSize = 50;
            maxStartSize = 80;
            minEndSize = 90;
            maxEndSize = 110 ;

            gravity = Vector2.Zero;

            minColor = new Color(200, 200, 200, 255);
            maxColor = new Color(255, 255, 255, 255);
        }

        public override GameEntity CreateClone()
        {
            //create and initialise particle system
            ParticleSystem system = new ParticleExplosion(texture);
            system.Initialise();

            return system;
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το μόνο που κάνει στην ουσία είναι να override την IntialiseSystem και να δίνει δικές τις τιμές στις μεταβλητές του συστήματος. Η παρακάτω κλάση δημιουργεί σπίθες (sparks):<br />
</span></p>
<pre class="brush: csharp;">
   class ParticleSparks : ParticleSystem
    {
        public ParticleSparks(Texture2D texture) : base(texture)
        {
        }

        protected override void InitialiseSystem()
        {
            //set params specific to the explosion particle system
            maxParticles = 100;
            duration = 0.5f;

            minHorizontalSpeed = -500;
            maxHorizontalSpeed = 500;
            minVerticalSpeed = -500;
            maxVerticalSpeed = 500;

            minRotationSpeed = 0;
            maxRotationSpeed = 0;
            minTimeToLive = 0.4f;
            maxTimeToLive = 0.6f;

            minStartSize = 6;
            maxStartSize = 6;
            minEndSize = 6;
            maxEndSize = 6;

            gravity = Vector2.Zero;

            minColor = Color.White;
            maxColor = Color.White;
        }

        public override GameEntity CreateClone()
        {
            //create and initialise particle system
            ParticleSystem system = new ParticleSparks(texture);
            system.Initialise();

            return system;
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Ούτε αυτή κάνει κάτι διαφορετικό αλλά το τελικό εφφέ είναι εντελώς διαφορετικό από την έκρηξη. Μπορούμε να το πάμε ακόμα πιο μακριά δημιουργώντας με ελάχιστες αλλαγές μια φωτιά τουρμπίνας αεροπλάνου (trail):<br />
</span></p>
<pre class="brush: csharp;">
  class ParticleTrail : ParticleSystem
    {
        public ParticleTrail(Texture2D texture)
            : base(texture)
        {
        }

        protected override void InitialiseSystem()
        {
            //set params specific to the explosion particle system
            maxParticles = 100;
            alwaysActive = true;
            rate = 80;

            minHorizontalSpeed = 0;
            maxHorizontalSpeed = 0;
            minVerticalSpeed = -100;
            maxVerticalSpeed = 0;

            minRotationSpeed = -3;
            maxRotationSpeed = 3;
            minTimeToLive = 0.5f;
            maxTimeToLive = 1.0f;

            minStartSize = 20;
            maxStartSize = 30;
            minEndSize = 5;
            maxEndSize = 5;

            gravity = Vector2.Zero;

            minColor = new Color(200, 200, 200, 255);
            maxColor = new Color(255, 255, 255, 255);
        }

        public override GameEntity CreateClone()
        {
            //create and initialise particle system
            ParticleSystem system = new ParticleTrail(texture);
            system.Initialise();

            return system;
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η βασική διαφορά (εκτός φυσικά από το εύρος των τιμών) είναι ότι θέτουμε την μεταβλητή alwaysActive σε true μιας και δεν θέλουμε να σβήσει η φωτιά της μηχανής.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Σε όλα τα παραπάνω εφέ η πηγή στα συστήματα σωματιδίων ήταν συγκεκριμένη (αν και μπορεί να μετακινούνταν). Με μικρές πάλι αλλαγές μπορούμε με το ίδιο μηχανισμό να φτιάξουμε εφέ όπως βροχή αστεροειδών η οποία δεν έχει συγκεκριμένη πηγή.<br />
</span></p>
<pre class="brush: csharp;">
    class ParticleRain : ParticleSystem
    {
        public ParticleRain(Texture2D texture)
            : base(texture)
        {
        }

        protected override void InitialiseSystem()
        {
            //set params specific to the explosion particle system
            maxParticles = 400;
            alwaysActive = true;
            rate = 80;

            minHorizontalSpeed = 0;
            maxHorizontalSpeed = 0;
            minVerticalSpeed = 0;
            maxVerticalSpeed = 1000;

            minRotationSpeed = 0;
            maxRotationSpeed = 0;
            minTimeToLive = 10f;
            maxTimeToLive = 10f;

            minStartSize = 6;
            maxStartSize = 6;
            minEndSize = 6;
            maxEndSize = 6;

            gravity = Vector2.Zero;

            minColor = Color.White;
            maxColor = Color.White;
        }

        public override void Update(GameTime gameTime, Rectangle viewport)
        {
            position = new Vector2((float)(random.NextDouble() * viewport.Width), 0);

            base.Update(gameTime, viewport);
        }

        public override GameEntity CreateClone()
        {
            //create and initialise particle system
            ParticleSystem system = new ParticleRain(texture);
            system.Initialise();

            return system;
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το επιπλέον που έκανα στην περίπτωση αυτή ήταν να override και την Update του ParticleSystem, και να αλλάζω τυχαία την θέση της «πηγής» έτσι ώστε να δημιουργεί νέα σωματίδια σε διαφορετική θέση κάθε φορά. Οι δυνατότητες του τι μπορείτε να πετύχετε είναι απεριόριστες! <img src='http://s2.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τελικά κάθε άλλο παρά σύντομο βγήκε και άρθρο και δεν μπήκα και σε λεπτομέρειες υλοποίησης. Για να ολοκληρώσω αξίζει να αναφερθώ στην κλάση ParticleManager την οποίας η λειτουργία είναι να συγκεντρώνει και να ελέγχει όλα τα διαφορετικά συστήματα σωματιδίων που  υπάρχουν στο παιχνίδι. Και αυτή λειτουργεί με την λογική την προ-δημιουργίας συστημάτων σωματιδίων και επαναχρησιμοποίησης για την αποφυγή δέσμευσης/αποδέσμευσης μνήμης κατά τη διάρκεια του game loop. Είναι και αυτή Singleton όπως και οι υπόλοιποι managers του παιχνιδιού μιας και χρειάζομαι μόνο ένα.<br />
</span></p>
<pre class="brush: csharp;">
    class ParticleManager
    {
        protected Dictionary&gt; particleSystems;

        static protected ParticleManager particleManager = null;
        static public ParticleManager Manager
        {
            get
            {
                if (particleManager == null)
                {
                    particleManager = new ParticleManager();
                }

                return particleManager;
            }
        }

        protected ParticleManager()
        {
            particleSystems = new Dictionary&gt;(10);
        }

        public void RegisterParticleSystem(string name)
        {
            if (!particleSystems.ContainsKey(name))
            {
                //preallocate particle system list only when no list exists for that name

                //I am expecting at most 50 systems of this type on screen at the same time.
                List list = new List(50);
                for (int i = 0; i &lt; 50; i++)
                {
                    list.Add((ParticleSystem)GameEntityFactory.CreateEntity(name));
                }
                //add list to dictionary
                particleSystems.Add(name, list);
            }
        }

        public ParticleSystem AddParticleSystem(string name, Vector2 position, Vector2 velocity)
        {
            List list;
            if (particleSystems.TryGetValue(name, out list))
            {
                //grab first dead particle system of this type
                foreach (ParticleSystem system in list)
                {
                    if (!system.Active)
                    {
                        system.Reset(position, velocity);
                        return system;
                    }
                }
            }

            return null;
        }

        public void Reset()
        {
            //erase everything
            particleSystems.Clear();
        }

        public void Update(GameTime gameTime, Rectangle viewport)
        {
            //update all particle systems
            foreach (KeyValuePair&gt; pair in particleSystems)
            {
                foreach (ParticleSystem system in pair.Value)
                {
                    system.Update(gameTime, viewport);
                }
            }
        }

        public void Render(SpriteBatch sb)
        {
            //render all particle systems
            foreach (KeyValuePair&gt; pair in particleSystems)
            {
                foreach (ParticleSystem system in pair.Value)
                {
                    system.Render(sb);
                }
            }
        }
    }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Για να χρησιμοποιήσουμε τα συστήματα σωματιδίων πρέπει αρχικά (στην LoadContent) να φτιάξουμε τα template και να τα κάνουμε register στο GameEntityFactory:<br />
</span></p>
<pre class="brush: csharp;">
 //register template for various particle systems
GameEntityFactory.RegisterEntityCategory(&quot;Explosion&quot;, new ParticleExplosion(Content.Load(&quot;Textures/fire&quot;)));
GameEntityFactory.RegisterEntityCategory(&quot;Trail&quot;, new ParticleTrail(Content.Load(&quot;Textures/fire&quot;)));
GameEntityFactory.RegisterEntityCategory(&quot;Sparks&quot;, new ParticleSparks(Content.Load(&quot;Textures/spark&quot;)));
GameEntityFactory.RegisterEntityCategory(&quot;Rain&quot;, new ParticleRain(Content.Load(&quot;Textures/spark&quot;)));
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Εδώ ορίζουμε και τις υφές που θα χρησιμοποιούν. Στην συνέχεια (στην loadGameLevel) πρέπει να ειδοποιήσουμε το ParticleManager για την ύπαρξη τους:<br />
</span></p>
<pre class="brush: csharp;">
ParticleManager.Manager.RegisterParticleSystem(&quot;Explosion&quot;);
ParticleManager.Manager.RegisterParticleSystem(&quot;Sparks&quot;);
ParticleManager.Manager.RegisterParticleSystem(&quot;Rain&quot;);
ParticleManager.Manager.RegisterParticleSystem(&quot;Trail&quot;);
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Κάθε φορά που θέλουμε να δημιουργήσουμε ένα εφέ το μόνο που έχουμε να κάνουμε είναι να καλέσουμε τη μέθοδο AddParticleSystem του ParticleManager. Για παράδειγμα αν θέλω να προσθέσω μια έκρηξη με σπίθες κάθε φορά που καταστρέφεται ένας εχθρός θα κάνω τις παρακάτω προσθήκες στην κλάση EnemyShip:<br />
</span></p>
<pre class="brush: csharp;">
        public int ApplyDamage(int damage)
        {
            shield -= damage;
            hitFeedbackElapsed = 0.1;
            hitFeedback = true;

            if (shield &lt;= 0)
            {
                status = Status.Dead;
                ParticleManager.Manager.AddParticleSystem(&quot;Explosion&quot;, new Vector2(rectangle.Center.X,rectangle.Center.Y), Vector2.Zero);
                ParticleManager.Manager.AddParticleSystem(&quot;Sparks&quot;, new Vector2(rectangle.Center.X, rectangle.Center.Y), Vector2.Zero);
                return maxShield;
            }

            return 0;
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Και αυτό ήταν! Φυσικά δεν ξεχνάμε να κάνουμε Update και Render το ParticleManager αντικείμενο στις αντίστοιχες μεθόδους του παιχνιδιού.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η παρακάτω εικόνα δείχνει τα 4 συστήματα σωματιδίων (έκρηξη, σπίθες, trail, και βροχή) του παιχνιδιού σε δράση:<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2011/03/030411_0822_notforsale1.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ο κώδικας είναι διαθέσιμος στο Code Repository σε <a href="http://code.google.com/p/videogameslab/downloads/detail?name=NotForSale_ParticleSystems.zip&amp;can=2&amp;q=" target="_blank">zip μορφή</a>.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μελετήστε το κώδικα του παιχνιδιού ως τώρα, σκεφτείτε αλλαγές και βελτιώσεις, και μείνετε συντονισμένοι γιατί ακολουθεί ανακοίνωση στις αρχές της επόμενης εβδομάδας!</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/585/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/585/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/585/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=585&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2011/03/04/not-for-sale-particle-systems/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2011/03/030411_0822_notforsale1.png" medium="image" />
	</item>
		<item>
		<title>Not for Sale! Ανίχνευση συγκρούσεων</title>
		<link>http://videogameslab.wordpress.com/2010/12/08/not-for-sale-collision-detection/</link>
		<comments>http://videogameslab.wordpress.com/2010/12/08/not-for-sale-collision-detection/#comments</comments>
		<pubDate>Wed, 08 Dec 2010 09:34:56 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=578</guid>
		<description><![CDATA[Στο σημερινό άρθρο είχα σκοπό να μελετήσουμε ανίχνευση συγκρούσεων και εκρήξεις με τη χρήση particle systems. Όμως, μιας και τελικά ο κώδικας για τα particle systems βγήκε μεγάλος και θέλει αρκετή επεξήγηση, θα δούμε σήμερα μόνο την ανίχνευση συγκρούσεων και θα αφήσουμε τις εκρήξεις για το επόμενο άρθρο. Η ανίχνευση σύγκρουσης (collision detection) καθώς και [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=578&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Στο σημερινό άρθρο είχα σκοπό να μελετήσουμε ανίχνευση συγκρούσεων και εκρήξεις με τη χρήση particle systems. Όμως, μιας και τελικά ο κώδικας για τα particle systems βγήκε μεγάλος και θέλει αρκετή επεξήγηση, θα δούμε σήμερα μόνο την ανίχνευση συγκρούσεων και θα αφήσουμε τις εκρήξεις για το επόμενο άρθρο.</span><br />
<span id="more-578"></span><br />
<span style="font-family:Verdana;font-size:9pt;">Η ανίχνευση σύγκρουσης (collision detection) καθώς και η απόκριση σε αυτή (collision response) είναι από τα πλέον ουσιαστικά συστατικά ενός παιχνιδιού καθώς επιτρέπει στο παίκτη να αλληλεπιδράσει με τον κόσμο το παιχνιδιού. Επιπλέον κάνει ένα παιχνίδι περισσότερο αληθοφανή μη επιτρέποντας για παράδειγμα ένα  χαρακτήρα να περάσει μέσα από ένα τοίχο ή αναγκάζοντας τον να «πατάει» πάνω σε μια πλατφόρμα.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η ανίχνευση συγκρούσεων μπορεί να είναι πολύ απλή ή πολύ περίπλοκη ανάλογα με το παιχνίδι. Στα παιχνίδια με τριδιάστατα γραφικά η ανίχνευση σύγκρουσης είναι συνήθως ιεραρχική ξεκινώντας από απλούστερα σχήματα (κύβος, σφαίρα) που περικλείουν τα τριδιάστατα μοντέλα (bounding boxes) και φτάνοντας μέχρι επίπεδο πολυγώνου αν χρειαστεί. Στην δική μας περίπτωση το παιχνίδι υποστηρίζει μόνο sprites και η ανίχνευση μπορεί να γίνει σε 2 επίπεδα, είτε σε επίπεδο rectangle είτε σε επίπεδο pixel. Στην πρώτη περίπτωση αρκεί να διαπιστώσουμε αν το Rectangle κάποιου sprite επικαλύπτει κάποιου άλλου, μια απλή και γρήγορη διαδικασία. Στην δεύτερη περίπτωση θα πρέπει να αποκτήσουμε πρόσβαση στα pixels ενός sprite και να διαπιστώσουμε αν υπάρχει επικάλυψη μεταξύ 2 sprites ελέγχοντας τα αντίστοιχα pixel. Η πρώτη περίπτωση είναι γρήγορη στην εκτέλεση αλλά μπορεί να μην επιστρέψει πάντα σωστό αποτέλεσμα (πχ μπορεί τα 2 rectangles να επικαλύπτονται σε κάποιο σημείο αλλά το σημείο αυτό να αντιστοιχεί σε κενό, να μην έχει εικόνα sprite δηλαδή). Η δεύτερη προσφέρει μεγάλη ακρίβεια μιας και μπορεί να βρει επακριβώς την επικάλυψη αλλά είναι πιο αργή στην εκτέλεση, ιδιαίτερα αν πρέπει να ελέγξουμε κάθε sprite ενάντια σε κάθε άλλο (enemies, παίκτη και bullets)  χωρίς να χρησιμοποιήσουμε επιπλέον κάποια μέθοδο βελτιστοποίησης (όπως ανίχνευση ορατότητας, ανίχνευση απόστασης κλπ). Εμείς για το συγκεκριμένο παιχνίδι θα χρησιμοποιήσουμε την απλή ανίχνευση με τα rectangles. Παρεμπιπτόντως οι 2 μέθοδοι μπορούν να συνδυαστούν, κάνοντας ένα γρήγορο πέρασμα για ανίχνευση σύγκρουσης με rectangles και στην συνέχεια να κάνουμε ανίχνευση σε επίπεδο pixel για όσα sprites διαπιστώσουμε πιθανή σύγκρουση.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το ΧΝΑ μας παρέχει μια μέθοδο στη κλάση Rectangle (την Intersects) η οποία επιστρέφει αν 2 rectangles «συγκρούονται» ή όχι και σε αυτή θα βασίσουμε το απλό collision detection του παιχνιδιού. Για να ελέγξουμε λοιπόν αν κάποια σφαίρα παίκτη χτυπάει κάποιο εχθρικό διαστημόπλοιο θα προσθέσουμε μια μέθοδο DoCollisions στην κλάση EnemySpawner:<br />
</span></p>
<pre class="brush: csharp;">
        public void DoCollisions()
        {
            List playerBullets = BulletManager.Manager.PlayerBullets;

            foreach (EnemyShip enemy in enemies)
            {
                if (enemy.Alive)
                {
                    foreach (Bullet bullet in playerBullets)
                    {
                        if (bullet.Alive &amp;&amp; bullet.Collides(enemy))
                        {
                            int points = enemy.ApplyDamage(bullet.Damage);
                            if (points &gt; 0)
                            {
                                PlayerShip.playerShip.AddScore(points);
                            }
                            break;
                        }
                    }
                }
            }
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">H μέθοδος αυτή ζητάει από το BulletManager μια λίστα με τις σφαίρες που έχει ρίξει ο παίκτης. Στην συνέχεια για κάθε ενεργό διαστημόπλοιο εχθρού (enemy.Alive) ελέγχει αν αυτό συγκρούεται με κάποια ενεργή σφαίρα. Για να διαπιστώσουμε σύγκρουση χρησιμοποιούμε την μέθοδο Collides της κλάσης Bullet (η οποία με την σειρά της χρησιμοποιεί την Intersects της Rectangle):<br />
</span></p>
<pre class="brush: csharp;">
        public override bool Collides(GameEntity entity)
        {
            //check collision of live bullet with given entity
            bool collides = (status == Status.Alive &amp;&amp; rectangle.Intersects(entity.Rectangle));

            if (collides)
            {
                status = Status.Dead;
            }

            return collides;
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Επιπλέον αν διαπιστωθεί ότι η συγκεκριμένη σφαίρα συγκρούεται με κάτι, τότε την απενεργοποιούμε έτσι ώστε να μην εμφανίζεται πλέον στην οθόνη (status = <span style="color:#2b91af;">Status</span>.Dead).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Επιστρέφοντας στην DoCollisions, όταν τελικά ανιχνεύσουμε την σύγκρουση μιας σφαίρας με κάποιο διαστημόπλοιο εχθρού, εφαρμόζουμε την ζημιά που προκαλεί η σφαίρα αυτή (bullet.Damage) στο διαστημόπλοιο καλώντας την μέθοδο ApplyDamage της κλάσης EnemyShip:<br />
</span></p>
<pre class="brush: csharp;">
        public int ApplyDamage(int damage)
        {
            shield -= damage;
            hitFeedbackElapsed = 0.1;
            hitFeedback = true;

            if (shield &lt;= 0)
            {
                status = Status.Dead;
                return maxShield;
            }

            return 0;
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η μέθοδος αυτή παίρνει ως όρισμα τη ζημιά που προκαλεί η σφαίρα και την αφαιρεί από την ασπίδα του διαστημοπλοίου. Αν η ασπίδα μηδενιστεί, τότε το διαστημόπλοιο καταστρέφεται (</span><span style="font-family:Courier New;font-size:10pt;">status = <span style="color:#2b91af;">Status</span>.Dead</span><span style="font-family:Verdana;"><span style="font-size:9pt;">) και επιστρέφουμε την αρχική ποσότητα της ασπιδας (maxShield) ως βαθμούς που θα προστεθούν στο score του παίκτη. Αυτός ο μηχανισμός είναι σχεδιαστική επιλογη, και επιβραβεύει το παίκτη με περισσότερους πόντους όταν αυτός καταστρέψει έναν εχθρό με μεγαλύτερη ασπίδα (και συνεπώς πιο ισχυρό). Επίσης για να δώσουμε κάποια ανάδραση στο παίκτη όταν αυτός χτυπά έναν εχθρό, και αυτός δεν καταστρέφεται ακόμα, κατά τη σύγκρουση της σφαίρας ορίζουμε κάποια χρονική περίοδο (</span><span style="font-size:10pt;">hitFeedbackElapsed) κατά την οποία δίνουμε μια κόκκινη απόχρωση στο διαστημόπλοιο. Αυτό το επιτυχγάνουμε οριζοντας μια μεταβλητή spriteColor και αλλάζοντας την τιμή της σε κόκκινο για όσο διαρκεί το hitFeedbackElapsed. Στην Update της EnemyShip προσθέτουμε:<br />
</span></span></p>
<pre class="brush: csharp;">
                spriteColor = Color.White;
                if (hitFeedback)
                {
                    hitFeedbackElapsed -= gameTime.ElapsedGameTime.TotalSeconds;
                    spriteColor = Color.Red;
                    hitFeedback = hitFeedbackElapsed &gt; 0 ? true : false;
                }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Και απεικονίζουμε το sprite ως:<br />
</span></p>
<pre class="brush: csharp;">
        public override void Render(SpriteBatch spriteBatch)
        {
            if (status == Status.Alive)
            {
                spriteBatch.Draw(texture, rectangle, spriteColor);
            }
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Μια αντίστοιχη DoCollisions μέθοδο προσθέτουμε στην κλάση του διαστημοπλοίου του παίκτη PlayerShip:<br />
</span></p>
<pre class="brush: csharp;">
        public void DoCollisions()
        {
            //get all enemy bullets
            List enemyBullets = BulletManager.Manager.EnemyBullets;

            //go through the list and find any that collide with playership
            foreach (Bullet bullet in enemyBullets)
            {
                if (bullet.Alive &amp;&amp; bullet.Collides(this))
                {
                    //subtract bullet damage from shield
                    shield -= bullet.Damage;
                    hitFeedbackElapsed = 0.1;
                    hitFeedback = true;

                    if (shield &lt;= 0)
                    {
                        //shield reaches zero, remove a life
                        lives--;
                        shield = maxShield;

                        if (lives &lt; 0)
                        {
                            //life reached zero game over
                            status = Status.Dead;
                        }
                    }

                    break;
                }
            }
        }
</pre>
<p><span style="font-family:Verdana;font-size:10pt;">Στην περίπτωση αυτή εξετάζουμε τις σφαίρες του εχθρού (<span style="color:#2b91af;">BulletManager</span>.Manager.EnemyBullets) και αφαιρούμε την ζημιά της σφαίρας (bullet.Damage) από την ασπίδα του παίκτη. Όταν αυτή μηδενιστεί τότε την ανανεώνουμε και αφαιρούμε μια ζωή. Αν οι ζωές φτάσουν κάτω του μηδενός (lives&lt;0) τότε ο παίκτης πεθαίνει και το παιχνίδι τελειώνει. Και εδώ χρησιμοποιούμε την κόκκινη απόχρωση ως ανάδραση κάθε φορά που κάποιο laser εχθρού χτυπά το παίκτη.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:10pt;">Γενικότερες προσθήκες που έγιναν στο παιχνίδι είναι αυτό να τερματίζει όταν καταστραφεί ο παίκτης (υλοποίηση του gamestate Lose δηλαδή) και η δυνατότητα να ξαναξεκινά το παίχνιδι. Επίσης δείχνουμε πρόχειρα (με κείμενο) τα player stats στην οθόνη (ζωές, ασπίδα και σκορ). Τέλος, μετά από την πολύ σωστή υπόδειξη του darklynx, τώρα ο παίκτης αρκεί να κρατά το spacebar πατημένο για να πυροβολά, δεν χρειάζονται επαναληπτικά πατήματα του πλήκτρου.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:10pt;">Στο άρθρο αυτό υλοποιήσαμε per-bounding box ανίχνευση σύγκρουσης. Αν σας ενδιαφέρει η per-pixel, ακριβέστερη ανίχνευση υπάρχει μια ενδιαφέρουσα υλοποίηση που χρησιμοποιεί την GPU <a href="http://blogs.msdn.com/b/shawnhar/archive/2008/12/31/pixel-perfect-collision-detection-using-gpu-occlusion-queries.aspx" target="_blank">εδώ</a>. Διαφορετικά μπορείτε πάντα να αποκτήσετε πρόσβαση στα pixel μιας υφής με την μέθοδο <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.texture2d.getdata%28v=XNAGameStudio.31%29.aspx" target="_blank">GetData</a> της Texture2D και να υλοποιήσετε την δική σας μέθοδο per-pixel collision (η οποία ανάλογα με τον αριθμό των συγκρούσεων ενδέχεται να είναι κάπως αργή).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:10pt;">Ο κώδικας του παιχνιδιού βρίσκεται στο Code Repository σε <a href="http://videogameslab.googlecode.com/files/NotForSale_CollisionDetection.zip" target="_blank">zip</a> μόνο μορφή μέχρι να εγκαταστήσω πάλι το SVN client που έχασα λόγω της πρόσφατης αναβάθμισης σε Windows 7.</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/578/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/578/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/578/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=578&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2010/12/08/not-for-sale-collision-detection/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>
	</item>
		<item>
		<title>Not for Sale! Βροχή από σφαίρες</title>
		<link>http://videogameslab.wordpress.com/2010/10/20/not-for-sale-bullet-rain/</link>
		<comments>http://videogameslab.wordpress.com/2010/10/20/not-for-sale-bullet-rain/#comments</comments>
		<pubDate>Wed, 20 Oct 2010 08:24:20 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=565</guid>
		<description><![CDATA[Συνεχίζουμε μετά από ένα καλό διάλλειμα την ανάπτυξη του παιχνιδιού Not for Sale!. Γνωρίζω ότι τα άρθρα έχουν αραιώσει περισσότερο από ότι θα ήθελα, το καλοκαίρι ήταν από πλευράς δουλειάς πολύ γεμάτο και το φθινόπωρο μπήκε και αυτό δυναμικά. Παρεμπιπτόντως χάρηκα που γνώρισα αρκετούς από τους αναγνώστες του blog στο 3ο HGDC και τα καλά [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=565&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Συνεχίζουμε μετά από ένα καλό διάλλειμα την ανάπτυξη του παιχνιδιού Not for Sale!. Γνωρίζω ότι τα άρθρα έχουν αραιώσει περισσότερο από ότι θα ήθελα, το καλοκαίρι ήταν από πλευράς δουλειάς πολύ γεμάτο και το φθινόπωρο μπήκε και αυτό δυναμικά. Παρεμπιπτόντως χάρηκα που γνώρισα αρκετούς από τους αναγνώστες του blog στο 3<sup>ο</sup> HGDC και τα καλά σας λόγια σχετικά με το Videogames Laboratory με εξέπληξαν και με ευχαρίστησαν  πολύ. Χαίρομαι που υπάρχει κοινό που βρίσκει το blog έστω και λίγο χρήσιμο, και αυτό με παρακινεί να συνεχίζω την ενασχόληση μου με αυτό.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Στο σημερινό tutorial θα προσθέσουμε στο παιχνίδι την δυνατότητα να ρίχνει ο παίκτης και οι εχθροί ακτίνες laser. Για να το υλοποιήσουμε αυτό χρειαζόμαστε 2 στοιχεία, μια κλάση Bullet που θα αντιπροσωπεύει μια ακτίνα laser στο παιχνίδι (ανεξαρτήτως αν είναι του παίκτη ή του εχθρού), και μια κλάση BulletManager η οποία θα διαχειρίζεται τις ακτίνες laser. Και με αυτό εννοώ ότι θα επιτρέπει την προσθήκη μιας ακτίνα laser, θα ανανεώνει την θέση της, θα ελέγχει (στο επόμενο tutorial) συγκρούσεις, θα την απεικονίζει και θα την καταστρέφει όταν αυτή βγει εκτός οθόνης.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;"><span id="more-565"></span>Ξεκινάμε καταρχάς με την κλάση Bullet η οποία κληρονομεί, όπως όλα τα αντικείμενα του παιχνιδιού, από την GameEntity:<br />
</span></p>
<pre class="brush: csharp;">
class Bullet : GameEntity
{
	public enum Status
	{
		Hot,
		Cold
	};

	protected Status status;
	protected int damage;
	protected Vector2 direction;

	public bool Alive
	{
		get
		{
			return status == Status.Hot;
		}
	}

	public Bullet(Texture2D texture, Rectangle rectangle, Vector2 direction, int speed, int damage)
		: base(texture, rectangle, speed)
	{
		this.damage = damage;
		this.direction = direction;
		status = Status.Cold;
	}

	public override void Update(GameTime gameTime, Rectangle viewport)
	{
		//only update active bullets
		if (status == Status.Hot)
		{
			//update only Y coord for now, we can later do X as well for sideways bullets
			rectangle.Y += (int)(direction.Y * speed * gameTime.ElapsedGameTime.TotalSeconds);

			//check if bullet is within viewport
			if (!rectangle.Intersects(viewport))
			{
				status = Status.Cold;
			}
		}
	}

	public virtual void Reset(Vector2 position)
	{
		//reset position and activate bullet
		rectangle.X = (int)position.X;
		rectangle.Y = (int)position.Y;
		status = Status.Hot;
	}

	public override void Render(SpriteBatch spriteBatch)
	{
		//only render active bullets
		if (status == Status.Hot)
		{
			spriteBatch.Draw(texture, rectangle, Color.White);
		}
	}

	public override bool Collides(GameEntity entity)
	{
		//check collision of live bullet with given entity
		return status == Status.Hot &amp;&amp; rectangle.Intersects(entity.Rectangle);
	}

	public override GameEntity CreateClone()
	{
		return new Bullet(texture, rectangle, direction, speed, damage);
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση αυτή ορίζει τις μεταβλητές status, damage και direction. Η status εκφράζει το αν το αντικείμενο είναι ενεργό ή όχι (Hot είναι η σφαίρα που μόλις έχει βγει από τη κάνη του όπλου και μπορεί να προκαλέσει ζημιά ενώ Cold γίνεται όταν έχει απομακρυνθεί πολύ και δεν έχει την ενέργεια πλέον να χτυπήσει κάτι). Η damage κρατά τη ζημιά που μπορεί να προκαλέσει η σφαίρα όταν χτυπήσει τον αντίπαλο και το direction είναι το (μοναδιαίο) διάνυσμα που περιγράφει την κατεύθυνση της σφαίρας.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Πέρα από τις μεταβλητές, η κλάση ορίζει και μια constructor μέθοδο για αρχικοποίηση, μια μέθοδο Update για ανανέωση της θέσης της σφαίρας, μια Reset για επαναρχικοποίηση μιας Cold σφαίρας, μια μέθοδο Render για την απεικόνιση, μια μέθοδο Collide για ανίχνευση σύγκρουσης και την CreateClone που δημιουργεί ένα αντίγραφο του template μιας σφαίρας που θα κάνουμε αργότερα register στο Entity Factory.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Από τις μεθόδους αυτές αξιοπρόσεκτη ίσως είναι μόνο η Update η οποία μετακινεί μια (ενεργή) σφαίρα στην κατεύθυνση που ορίζει το διάνυσμα direction με ταχύτητα που καθορίζει η μεταβλητή speed. Προσοχή, η ταχύτητα που ορίζει η speed είναι σε pixel/second γι αυτό θα πρέπει να πολλάπλασιάσουμε με το χρονικό διάστημα που πέρασε από το προηγούμενο καρέ gameTime.ElapsedGameTime.TotalSeconds για να υπολογίσουμε τον αριθμό των pixel που πρέπει να μετακινήσουμε τη σφαίρα σε αυτό το βήμα. Ο λόγος που ορίζουμε την ταχύτητα σε pixel/second είναι για να κάνουμε την κίνηση της σφαίρας ανεξάρτητη από το framerate του παιχνιδιού. Επιπλέον η Update ελέγχει αν η δεδομένη σφαίρα είναι εντός ή εκτός οθόνης και σε περίπτωση που είναι εκτός αλλάζει το status της σε Cold έτσι ώστε να πάψει το παιχνίδι να την ανανεώνει και να την απεικονίζει (και να ελέγχει συγκρούσεις με αυτή).</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση Bullet είναι έτοιμη προς χρήση, προκειμένου να γίνει αυτό όμως πρέπει να κάνουμε register templates για τις σφαίρες του εχθρού και του παίκτη στο Entity Factory. Όπως και με τα υπόλοιπα αντικείμενα παιχνιδιού αυτό θα γίνει στην LoadContent:<br />
</span></p>
<pre class="brush: csharp;">
protected override void LoadContent()
{
	//υπόλοιπος κώδικας...

	//register template objects for bullets
	GameEntityFactory.RegisterEntityCategory(&quot;PlayerBullet&quot;, new Bullet(Content.Load(&quot;Textures/blast&quot;), new Rectangle(0, 0, 7, 20), new Vector2(0,-1),300,50));
	GameEntityFactory.RegisterEntityCategory(&quot;EnemyBullet&quot;, new Bullet(Content.Load(&quot;Textures/enemyblast&quot;), new Rectangle(0, 0, 7, 20), new Vector2(0, 1), 400, 10));

	//υπόλοιπος κώδικας...

}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το πρότυπο σφαίρας του παίκτη θα έχει όνομα &#8220;PlayerBullet&#8221;, θα έχει κατεύθυνση προς τα πάνω (0,-1), ταχύτητα 300 pixel/second και θα προκαλεί ζημιά 50HP (health points) στον εχθρό. Αντίστοιχα το πρότυπο σφαίρας εχθρού θα έχει όνομα &#8220;EnemyBullet&#8221;, κατεύθυνση προς τα κάτω (0,1), ταχύτητα 400 pixel/second και θα προκαλεί ζημιά 10HP στο παίκτη. Αναλόγως μπορούμε να ορίσουμε πολλά διαφορετικά είδη σφαιρών, με διαφορετικά μεγέθη, υφές, ταχύτητες, ζημιά κλπ.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Εκτός της κλάσης Bullet, χρειαζόμαστε και κάποιο τρόπο να προσθέτουμε σφαίρες στη σκηνή, να τις ανανεώνουμε, να τις απεικονίζουμε και να ελέγχουμε συγκρούσεις, χρειαζόμαστε δηλαδή (όπως κάναμε και με άλλα αντικείμενα του παιχνιδιού) έναν BulletManager.<br />
</span></p>
<pre class="brush: csharp;">
class BulletManager
{
	protected List playerBullets;
	protected List enemyBullets;

	protected static BulletManager bulletManager = null;
	static public BulletManager Manager
	{
		get
		{
			//if bulletmanager is not created, do it now
			if (bulletManager == null)
			{
				bulletManager = new BulletManager();
			}

			return bulletManager;
		}
	}

	public BulletManager()
	{
		int playerBulletMax = 50;
		int enemyBulletMax = 150;

		playerBullets = new List(playerBulletMax);
		enemyBullets = new List(enemyBulletMax);

		//preallocate player bullets
		for (int i = 0; i &lt; playerBulletMax; i++)
		{
			playerBullets.Add((Bullet)GameEntityFactory.CreateEntity(&quot;PlayerBullet&quot;));
		}

		//preallocate enemy bullets
		for (int i = 0; i &lt; enemyBulletMax; i++)
		{
			enemyBullets.Add((Bullet)GameEntityFactory.CreateEntity(&quot;EnemyBullet&quot;));
		}

	}

	public void AddEnemyBullet(Vector2 position)
	{
		//find an inactive enemy bullet and activate it
		foreach (Bullet bullet in enemyBullets)
		{
			if (!bullet.Alive)
			{
				bullet.Reset(position);
				break;
			}
		}
	}

	public void AddPlayerBullet(Vector2 position)
	{
		//find an inactive player bullet and activate it
		foreach (Bullet bullet in playerBullets)
		{
			if (!bullet.Alive)
			{
				bullet.Reset(position);
				break;
			}
		}
	}

	public void Update(GameTime gameTime, Rectangle viewport)
	{
		//update enemy bullets
		foreach (Bullet bullet in enemyBullets)
		{
			bullet.Update(gameTime, viewport);
		}

		//update player bullets
		foreach (Bullet bullet in playerBullets)
		{
			bullet.Update(gameTime, viewport);
		}
	}

	public void Render(SpriteBatch sb)
	{
		//render player bullets
		foreach (Bullet bullet in playerBullets)
		{
			bullet.Render(sb);
		}

		//render enemy bullets
		foreach (Bullet bullet in enemyBullets)
		{
			bullet.Render(sb);
		}
	}

}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η BulletManager διατηρεί 2 αντικείμενα τύπου List, ένα για τις σφαίρες του παίκτη (playerBullets) και ένα για του εχθρού (enenyBullets). Θα μπορούσαμε να είχαμε και μία λίστα με όλες τις σφαίρες ανεξαρτήτου προέλευσης αλλά με τον τρόπο που επιλέξαμε θα γίνει απλούστερη, ίσως και πιο γρήγορη, η ανίχνευση συγκρούσεων αργότερα. Πέρα από αυτό η κλάση ορίζει μεθόδους για προσθήκη σφαίρας στην αντίστοιχη λίστα ανάλογα με το ποιος πυροβολεί (AddPlayerBullet και AddEnemyBullet), μια μέθοδο για Update των σφαιρών, και μια για απεικόνιση των (Render).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Πρέπει να παρατηρήσουμε ότι στο constructor δημιουργούμε όχι μόνο το αντικείμενο της λίστας για κάθε είδος σφαίρας, αλλά και τις σφαίρες τις ίδιες.<br />
</span></p>
<pre class="brush: csharp;">
	public BulletManager()
	{
		int playerBulletMax = 50;
		int enemyBulletMax = 150;

		playerBullets = new List(playerBulletMax);
		enemyBullets = new List(enemyBulletMax);

		//preallocate player bullets
		for (int i = 0; i &lt; playerBulletMax; i++)
		{
			playerBullets.Add((Bullet)GameEntityFactory.CreateEntity(&quot;PlayerBullet&quot;));
		}

		//preallocate enemy bullets
		for (int i = 0; i &lt; enemyBulletMax; i++)
		{
			enemyBullets.Add((Bullet)GameEntityFactory.CreateEntity(&quot;EnemyBullet&quot;));
		}

	}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η κάθε σφαίρα όταν δημιουργείται είναι ανενεργή εξορισμού και δεν εμφανίζεται στην οθόνη. Στην συνέχεια όταν θέλουμε να «προσθέσουμε» μια νέα σφαίρα στην σκηνή με τη χρήση των AddPlayerBullet και AddEnemyBullet, διατρέχουμε την αντίστοιχη λίστα και επαναρχικοποιούμε τη πρώτη ανενεργή σφαίρα που συναντούμε.<br />
</span></p>
<pre class="brush: csharp;">
	public void AddEnemyBullet(Vector2 position)
	{
		//find an inactive enemy bullet and activate it
		foreach (Bullet bullet in enemyBullets)
		{
			if (!bullet.Alive)
			{
				bullet.Reset(position);
				break;
			}
		}
	}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Ο λόγος που το κάνουμε αυτό είναι για να αποφύγουμε την δέσμευση/αποδέσμευση μνήμης κατά τη διάρκεια του game loop έτσι ώστε να μην «ξυπνήσουμε» τον garbage collector, κάτι που ίσως μειώσει στιγμιαία το framerate του παιχνιδιού.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Στις μεθόδους Update και Render δεν αξίζει να γίνει αναφορά, απλά διατρέχουν τις λίστες και καλούν τις αντίστοιχες Update και Render των αντικειμένων Bullet. Αυτό στο οποίο αξίζει να κάνουμε αναφορά είναι στον ορισμό ενός static αντικειμένου τύπου BulletManager, μέσα στην ίδια την BulletManager καθώς και του property που δίνει πρόσβαση σε αυτό εκτός της κλάσης.</span></p>
<pre class="brush: csharp;">
	protected static BulletManager bulletManager = null;
	static public BulletManager Manager
	{
		get
		{
			//if bulletmanager is not created, do it now
			if (bulletManager == null)
			{
				bulletManager = new BulletManager();
			}

			return bulletManager;
		}
	}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στο παιχνίδι υπάρχει ανάγκη για ένα μόνο αντικείμενο κλάσης BulletManager. Για δική μας ευκολία, και να για να είναι προσβάσιμο το αντικείμενο αυτό από διαφορετικά σημεία του παιχνιδιού, χωρίς να πρέπει να διακινώ αναφορές (references) σε αυτό, μπορώ να το ορίσω ως static στην κλάση BulletManager την ίδια. Για να δώσω πρόσβαση στο bulletManager δημιούργησα επίσης ένα static property Manager για αυτή τη δουλειά. Το property Manager έχει και επιπλέον λειτουργία, ελέγχει αν έχει ήδη δημιουργηθεί το αντικείμενο bulletManager και αν όχι το δημιουργεί πριν το επιστρέψει. Με τον τρόπο αυτό για να έχω πρόσβαση στο bulletManager οπουδήποτε, το μόνο που έχω να κάνω είναι να καλέσω την BulletManager.Manager. Στον κώδικα του παιχνιδιού έχω μετατρέψει και τους υπόλοιπους managers έτσι ώστε να χρησιμοποιούν αυτό τον τρόπο δημιουργίας και πρόσβασης.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ο παίκτης πυροβολεί με το πλήκτρο space οπότε για να προσθέσουμε μια σφαίρα το μόνο που έχουμε να κάνουμε είναι η προσθήκη του παρακάτω κώδικα στη Update του PlayerShip:<br />
</span></p>
<pre class="brush: csharp;">
public void Update(GameTime gameTime, KeyboardState kbState, Rectangle viewport)
{
	// κώδικας...

	if (previousKbState.IsKeyUp(Keys.Space) &amp;&amp; kbState.IsKeyDown(Keys.Space))
	{
		//add a bullet on spacebar down
		BulletManager.Manager.AddPlayerBullet(new Vector2(rectangle.X+rectangle.Width/2, rectangle.Y+rectangle.Height/2));
	}

	//κώδικας…

	previousKbState = kbState;
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">H αρχική θέση της σφαίρας είναι η θέση του διαστημοπλοίου του παίκτη τη στιγμή που πυροβολεί. Για το διαστημόπλοιο του εχθρού, αλλάζουμε την Update της κλάσης EnemyShip ως εξής:<br />
</span></p>
<pre class="brush: csharp;">
public override void Update(GameTime gameTime, Rectangle viewport)
{
	if (status != SpaceShipStatus.Dead)
	{
		rectangle.Y += (int)(speed * gameTime.ElapsedGameTime.TotalSeconds);

		if (rectangle.Intersects(viewport))
		{
			timeToNextShot -= gameTime.ElapsedGameTime.TotalSeconds;

			if (timeToNextShot &lt;= 0)
			{
				 BulletManager.Manager.AddEnemyBullet(new Vector2(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2));
				 timeToNextShot = 1.0 / rate;
			}
		} 		

		if (rectangle.Top &gt; viewport.Height)
		{
			status = SpaceShipStatus.Dead;
		}
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Εδώ το διαστημόπλοιο πυροβολεί περιοδικά από μόνο του. Καταρχάς ελέγχουμε αν είναι ορατό το εχθρικό διαστημόπλοιο (για να μην μας έρχονται laser από διαστημόπλοια εκτός οθόνης) και αν ναι ελαττώνουμε το χρόνο timeToNextShot που μένει μέχρι τον επόμενο πυροβολισμό. Όταν αυτός φτάσει στο μηδέν τότε ο εχθρός πυροβολεί και επαναρχικοποιεί το χρόνο timeToNextShot.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τέλος για να κάνουμε Update και Render τις σφαίρες το μόνο που έχουμε να κάνουμε είναι να καλέσουμε τις αντίστοιχες μεθόδους του bulletManager στη κλάση Game1:<br />
</span></p>
<pre class="brush: csharp;">
        private void updateWorld(GameTime gameTime)
        {
            background.Update(gameTime);

            PlayerShip.Ship.Update(gameTime, Keyboard.GetState(), cameraViewport);
            EnemyManager.Manager.Update(gameTime, cameraViewport);
            BulletManager.Manager.Update(gameTime, cameraViewport);
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">και</span></p>
<pre class="brush: csharp;">
        private void renderWorld(SpriteBatch sb)
        {
            BulletManager.Manager.Render(sb);
            PlayerShip.Ship.Render(sb);
            EnemyManager.Manager.Render(sb);
        }
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Τρέχοντας το παιχνίδι μπορούμε να πυροβολήσουμε με το space και οι εχθροί μας πυροβολούν αντίστοιχα.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2010/10/102010_0824_notforsale1.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Δεν θα ασχοληθούμε με collision detection στο tutorial αυτό μιας και έχει ήδη αυξηθεί επικίνδυνα σε μέγεθος, θα το κάνουμε στο επόμενο. Ο κώδικας του σημερινού tutorial είναι όπως πάντα διαθέσιμος στο <a href="http://videogameslab.wordpress.com/2009/07/23/code-repository-2/" target="_blank">Code Repository</a> μέσω SVN αλλά και σε <a href="http://code.google.com/p/videogameslab/downloads/detail?name=NotForSale_part4.zip&amp;can=2&amp;q=" target="_blank">zip μορφή</a>.<br />
</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/565/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/565/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/565/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=565&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2010/10/20/not-for-sale-bullet-rain/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2010/10/102010_0824_notforsale1.png" medium="image" />
	</item>
		<item>
		<title>Not for Sale! Παίκτης  και εχθροί μέρος 2ο</title>
		<link>http://videogameslab.wordpress.com/2010/07/02/not-for-sale-enemies-2/</link>
		<comments>http://videogameslab.wordpress.com/2010/07/02/not-for-sale-enemies-2/#comments</comments>
		<pubDate>Fri, 02 Jul 2010 08:02:14 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=547</guid>
		<description><![CDATA[Συνεχίζουμε με το δεύτερο μέρος του tutorial προσθήκης παίκτη και εχθρών στο παιχνίδι Not for Sale! Στο πρώτο μέρος είχαμε ορίσει μια ιεραρχία από κλάσεις η οποία άρχιζε με τη κλάση GameEntity, από την οποία κληρονομούν όλα τα αντικείμενα του παιχνιδιού, και κατέληγε στις κλάσεις FighterShip και CargoShip οι οποίες αποτελούν τα πρότυπα για τα [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=547&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Συνεχίζουμε με το δεύτερο μέρος του tutorial προσθήκης παίκτη και εχθρών στο παιχνίδι Not for Sale! Στο πρώτο μέρος είχαμε ορίσει μια ιεραρχία από κλάσεις η οποία άρχιζε με τη κλάση GameEntity, από την οποία κληρονομούν όλα τα αντικείμενα του παιχνιδιού, και κατέληγε στις κλάσεις FighterShip και CargoShip οι οποίες αποτελούν τα πρότυπα για τα δυο είδη εχθρικών διαστημοπλοίων που έχουμε στο παιχνίδι και τη κλάση PlayerShip για το διαστημόπλοιο του παίκτη.</span></p>
<p><strong><span style="font-family:Verdana;font-size:9pt;">Σημείωση: επειδή με κάποια σατανική συνέργια του Word και του WordPress εγινε overwrite το πρώτο μέρος του tutorial, το έχω ανεβάσει σε <a href="http://videogameslab.files.wordpress.com/2010/07/not-for-sale-part-2.pdf" target="_blank">pdf μορφή</a> για όποιον το αναζητά.</span></strong></p>
<p><span id="more-547"></span><span style="font-family:Verdana;font-size:9pt;">Θα ήθελα να κάνω μια παρένθεση εδώ σχετικά με τη κίνηση των εχθρικών διαστημοπλοίων και συγκεκριμένα του FighterShip. Είπαμε ότι το διαστημόπλοιο αυτό «κυνηγά» το παίκτη με σκοπό να τον φέρει στο οπτικό του πεδίο και να τον πυροβολήσει. Αυτή τη συμπεριφορά υλοποιήσαμε και εμείς για το είδος αυτό εχθρού. Όμως γενικά, μπορούμε να δημιουργήσουμε μια ποικιλία κινήσεων εχθρού, εκμεταλλευόμενοι απλές συναρτήσεις. Για παράδειγμα, χρησιμοποιώντας την Υ συντεταγμένη της θέσης του εχθρού (position.Y) ως όρισμα σε μια συνάρτηση ημιτόνου, μπορούμε να κάνουμε τον εχθρό να ταλαντεύεται «αριστερά-δεξιά» καθώς αυτός κινείται προς το κάτω μέρος της οθόνης:<br />
</span></p>
<pre class="brush: csharp;">
rectangle.X = (int)(initPosition.X + Math.Sin(rectangle.Y * 0.01) * 100);
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">H initPosition είναι μια μεταβλητή τύπου Vector2 η οποία κρατά το κέντρο της ταλάντωσης (ορίζεται ως η αρχική position που περάσαμε ως όρισμα στον constructor του FighterShip).<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2010/07/070210_0801_notforsale11.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μπορούμε να χρησιμοποιήσουμε οποιαδήποτε συνάρτηση για να πετύχουμε μια μεγάλη ποικιλία κινήσεων. Επιπλέον δεν χρειάζεται να περιοριστούμε σε απλές συναρτήσεις, για παράδειγμα με τη παρακάτω συνάρτηση:<br />
</span></p>
<pre class="brush: csharp;">
if (rectangle.Y &gt; viewport.Height / 4 &amp;&amp; rectangle.Y &lt; 3 * viewport.Height / 4)
{
	double angle = 4 * Math.PI / viewport.Height;
	rectangle.X = (int)(initPosition.X + Math.Sin((rectangle.Y - viewport.Height / 4) * angle) * 150);
}
else
{
	rectangle.X = (int)initPosition.X;
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">μπορούμε να αναγκάσουμε τα εχθρικά διαστημόπλοια, καθώς κατεβαίνουν, να κάνουν την ίδια ημιτονοειδή ταλάντωση στο κέντρο της οθόνης και στην συνέχεια να επιστρέψουν στην αρχική τους πορεία.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2010/07/070210_0801_notforsale21.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το μόνο όριο όπως λένε είναι η φαντασία σας (σύμφωνοι, και οι Μαθηματικές σας γνώσεις). Κλείνει η παρένθεση και επανερχόμαστε.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η πιο απλή προσέγγιση για την δημιουργία και τοποθέτηση των εχθρών είναι να φτιάξουμε ένα List&lt;&gt; για κάθε είδος στην κλάση Game1 και να γεμίσουμε το List&lt;&gt; με αντικείμενα του κάθε τύπου εχθρού με τη χρήση του τελεστή new. Σε (ψευδο)κώδικα αυτό θα το εκφράζαμε ως εξής:<br />
</span></p>
<pre class="brush: csharp;">
List&lt;FighterShip&gt; fighterShips = new List&lt;FighterShip&gt;(15);
List&lt;CargoShip&gt; cargoShips = new List&lt;CargoShip&gt;(15);

for (int i = 0; i &lt; 15; i++)
{
    fighterShips.Add(new FighterShip(fighterTexture, rectangle1, 100, 5, 0.5, 100);
    cargoShips.Add(new CargoShip(cargoTexture, rectangle2, 200, 5, 0.2, 300);
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στην συνέχεια θα διατρέχαμε τις 2 λίστες κάθε φορά μου επιθυμούσαμε να κάνουμε Update και Render το κάθε εχθρικό διαστημόπλοιο.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Αυτός ο τρόπος όμως δημιουργίας εχθρών δεν είναι και ο πιο εύχρηστος, και είναι δύσκολο να διαχειριστείς πολλά διαφορετικά είδη διαστημοπλοίων έτσι. Επιπλέον λείπει η πληροφορία για το πόσο συχνά θα πρέπει να «δημιουργούμε» ένα νέο εχθρό, την αρχική θέση του κάθε εχθρού κλπ. Επίσης αν θέλαμε να δημιουργήσουμε «κύματα» εχθρικών διαστημοπλοίων θα ήταν αρκετά πιο δύσκολο με τη μέθοδο των απλών λιστών.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Γι αυτό στα πλαίσια του tutorial αυτού θα δημιουργήσουμε ένα πιο ευέλικτο και περισσότερο αυτοματοποιημένο σύστημα, κατάλληλο ίσως και για πιο πολύπλοκα παιχνίδια του είδους, ενσωματώνοντας όλη την απαραίτητη λειτουργικότητα που περιγράψαμε σε μια κλάση με το όνομα EnemySpawner.<br />
</span></p>
<pre class="brush: csharp;">
class EnemySpawner
{
	List&lt;EnemyShip&gt; enemies;
	Vector2 position;
	float period;
	double timeToNext;
	Random random;
	string category;

	public EnemySpawner(string category, Vector2 position, int maxNumber, float rate)
	{
	}

	public void Update(GameTime gameTime, Viewport viewport)
	{
	}

	public void Render(SpriteBatch sb)
	{
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Τα διαστημόπλοια που δημιουργεί και διαχειρίζεται ένα αντικείμενο τύπου EnemySpawner αποθηκεύονται σε ένα List από αντικείμενα τύπου EnemyShip. Θυμίζουμε ότι στην κλάση EnemyShip βασίζονται όλα τα εχθρικά διαστημόπλοια του παιχνιδιού.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Επιπλέον η EnemySpawner έχει, εκτός του constructor, 2 μεθόδους την Update και την Render. Στον constructor ορίζουμε την κατηγορία του εχθρικού διαστημοπλοίου (category) η οποία στην ουσία είναι το όνομα της αντίστοιχης κλάσης («CargoShip» ή «FighterShip» ή οποιαδήποτε κλάση εχθρού έχουμε δημιουργήσει). Επίσης η μέθοδος αυτή παίρνει ως ορίσματα την θέση (position) από την οποία θα ξεκινάνε τα διαστημόπλοια, το μέγιστο αριθμό τους (maxNumber) και το ρυθμό με τον οποίο θα εμφανίζονται στην οθόνη (rate). Κάθε διαστημόπλοιο εχθρού που δημιουργούμε το προσθέτουμε στην λίστα enemies που κρατά εσωτερικά η κλάση EnemySpawner. Η λίστα είναι τύπου List&lt;EnemyShip&gt; έτσι ώστε να μας επιτρέπει να αποθηκεύουμε αναφορές σε οποιαδήποτε κλάση εχθρού κληρονομεί την EnemyShip. Το πώς δημιουργούμε ένα νέο αντικείμενο εχθρού συγκεκριμένα θα το εξηγήσουμε αργότερα.<br />
</span></p>
<pre class="brush: csharp;">
class EnemySpawner
{
	public EnemySpawner(string category, Vector2 position, int maxNumber, float rate)
	{
		this.position = position;
		this.period = 1.0f / rate; // convert frequency to time
		this.category = category;

		enemies = new List&lt;EnemyShip&gt;(maxNumber);

		for (int i = 0; i &lt; maxNumber; i++)
		{
			//create enemy ship of given category
			GameEntity entity = GameEntityFactory.CreateEntity(category);

			//add ship to enemySpawner’s enemy list
			enemies.Add((EnemyShip)entity);

			//register enemy ship to GameEntity master list as well for easy access.
			GameEntity.AddEntity(&quot;Enemy&quot;, entity);
		}

		random = new Random();
	}

	public void Update(GameTime gameTime, Viewport viewport)
	{
	}

	public void Render(SpriteBatch sb)
	{
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στην μέθοδο Update διατρέχουμε τι λίστα με τα εχθρικά διαστημόπλοια και καλούμε την Update του καθενός. Στην συνέχεια ελέγχουμε αν είναι καιρός να δημιουργήσουμε ένα νέο διαστημόπλοιο και αν ναι, διατρέχουμε και πάλι τη λίστα και εντοπίζουμε το πρώτο «ανενεργό»  αντικείμενο. Το αντικείμενο αυτό το αρχικοποιούμε με τη θέση του EnemySpawner και το αφήνουμε ελεύθερο να εκπληρώσει το σατανικό σκοπό του. Αυτή τη παράξενη εντολή που θέτει τη position.Χ συντεταγμένη του EnemySpawner σε μια τυχαία τιμή θα την εξηγήσουμε σε λίγο (και αυτή).<br />
</span></p>
<pre class="brush: csharp;">
Class EnemySpawner
{
	public void Update(GameTime gameTime, Viewport viewport)
	{
		//reduce elapsed time to next enemy spawn
		timeToNext -= gameTime.ElapsedGameTime.TotalSeconds;

		//update all enemies in list
		foreach (EnemyShip ship in enemies)
		{
			ship.Update(gameTime, viewport);
		}

		if (timeToNext &lt;= 0)
		{
			//time to add a new enemy to the screen
			foreach (EnemyShip ship in enemies)
			{
				//search list to locate a “dead” enemy ship
				if (ship.Status == SpaceShipStatus.Dead)
				{
					//each enemy ship is initially placed at enemySpawner’s position
					ship.Reset(position);

					//set enemySpawner to a random X position for demo purposes
					position.X = random.Next(viewport.Width – ship.Rectangle.Width);
					break;
				}
			}
			timeToNext = period;
		}
	}

	public void Render(SpriteBatch sb)
	{
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Τέλος, η μέθοδος Render που απεικονίζει τα εχθρικά διαστημόπλοια του EnemySpawner στην οθόνη είναι τετριμμένη, απλά διατρέχουμε τη λίστα και καλούμε την αντίστοιχη μέθοδο Render κάθε αντικειμένου.<br />
</span></p>
<pre class="brush: csharp;">
Class EnemySpawner
{
	public EnemySpawner(string category, Vector2 position, int maxNumber, float rate)
	{
	}

	public void Update(GameTime gameTime, Viewport viewport)
	{
	}

	public void Render(SpriteBatch sb)
	{
		//render all active enemies
		foreach (EnemyShip ship in enemies)
		{
			ship.Render(sb);
		}
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Να εξηγήσουμε τώρα κάποια πράγματα που προσπέρασα παραπάνω. Καταρχάς πως δημιουργούμε τα αντικείμενα των εχθρικών διαστημοπλοίων. Αυτό το κάνουμε με μέσω της κλάσης GameFactory. Η κλάση αυτή βασίζεται σε ένα factory design pattern το οποίο μας επιτρέπει να δημιουργήσουμε ένα οποιοδήποτε αντικείμενο δίνοντας μόνο το όνομα του (στη συγκεκριμένη περίπτωση).<br />
</span></p>
<pre class="brush: csharp;">
class GameEntityFactory
{
	//dictionary that stores the template object for each entity category
	static Dictionary&lt;string, GameEntity&gt; registeredEntities = new Dictionary&lt;string, GameEntity&gt;(20);

	//register a template object for a category
	static public void RegisterEntityCategory(string category, GameEntity entity)
	{
		GameEntity temp = GetEntityTemplate(category);
		if (temp != null)
		{
			registeredEntities.Remove(category);
		}

		registeredEntities.Add(category, entity);
	}

	//create an entity based on the entity category
	static public GameEntity CreateEntity(string category)
	{
		GameEntity entity = GetEntityTemplate(category);
		if (entity != null)
		{
			return entity.CreateClone();
		}
		return null;
	}

	//retrieve a template object by category
	static public GameEntity GetEntityTemplate(string category)
	{
		GameEntity entity;
		if (registeredEntities.TryGetValue(category, out entity))
		{
			return entity;
		}
		return null;
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Υπάρχουν διάφοροι τρόποι να υλοποιήσεις ένα object factory. Σε γλώσσες όπως η C# (και η Java) που υποστηρίζουν μια τεχνολογία που ονομάζεται reflection, μας δίνεται η δυνατότητα (ανάμεσα σε άλλα) να δημιουργούμε νέα αντικείμενα με βάση το τύπο του και μόνο. Επίσης ίσως κάτι τέτοιο να ήταν εφικτό και με τη χρήση templates (generics στη C#), αλλά στην συγκεκριμένη εφαρμογή θα προτιμήσω μια πιο παραδοσιακή λύση.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση GameEntityFactory λειτουργεί σε 2 στάδια. Καταρχάς πρέπει να κάνουμε register κάθε τύπο αντικειμένου που θέλουμε να αναπαράγει. Αυτό το κάνουμε με τη χρήση της μεθόδου RegisterEntityCategory, η οποία παίρνει ως όρισμα το όνομα του αντικειμένου (&#8220;Cargo&#8221; ή &#8220;Fighter κλπ) και ένα «δείγμα» του αντικειμένου που θέλουμε να αναπαράγουμε. Στην συνέχεια αποθηκεύει αυτές τις 2 πληροφορίες σε ένα αντικείμενο τύπου Dictionary για εύκολη πρόσβαση.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Στην συνέχεια, το μόνο που έχουμε να κάνουμε για να δημιουργήσουμε ένα νέο αντικείμενο είναι να καλέσουμε τη μέθοδο CreateEntity και να περάσουμε ως όρισμα το όνομα του αντικειμένου. Η CreateEntity θα ανασύρει το «δείγμα» που αντιστοιχεί στο συγκεκριμένο όνομα και θα καλέσει την CreateClone μέθοδο του για να πάρει ένα πιστό αντίγραφο. Το αντίγραφο θα είναι το νέο αντικείμενο που δημιουργήσαμε. Πρέπει να αναφέρουμε εδώ ότι με factory class αυτό μπορούμε να δημιουργήσουμε οποιοδήποτε αντικείμενο κληρονομεί της κλάσεις GameEntity (όλα τα αντικείμενα του παιχνιδιού δηλαδή).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Ας δούμε ένα παράδειγμα χρήσης του τρόπου αυτού δημιουργίας αντικειμένου. Στη κυρίως κλάση του παιχνιδιού Game1 κάνουμε register τα δείγματα για τα 2 εχθρικά διαστημόπλοια ως εξής.<br />
</span></p>
<pre class="brush: csharp;">
//register enemy types
GameEntityFactory.RegisterEntityCategory(&quot;Cargo&quot;, new CargoShip(Content.Load&lt;Texture2D&gt;(&quot;Textures/cargoship2&quot;), new Rectangle(0, 0, 80, 120), 100, 2, 1.0f, 500));
GameEntityFactory.RegisterEntityCategory(&quot;Fighter&quot;, new FighterShip(Content.Load&lt;Texture2D&gt;(&quot;Textures/fightership2&quot;), new Rectangle(0,0,60, 70), 100, 5, 1.0f, 100));
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Τώρα, μπορώ να δημιουργήσω ένα αντικείμενο τύπου Cargo με τη χρήση της εντολής<br />
</span></p>
<pre class="brush: csharp;">
GameEntity entity = GameEntityFactory.CreateEntity(“Cargo”);
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το νέο αντικείμενο θα έχει την ίδια υφή, ταχύτητα, ασπίδα κλπ με το δείγμα Cargo. Αυτό ακριβώς είναι που κάνουμε στο constructor της κλάσης EnemySpawner λίγο παραπάνω.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μπορώ λοιπόν τώρα να δημιουργήσω μια γεννήτρια εχθρικών διαστημοπλοίων (πχ τύπου Cargo) και να την τοποθετήσω στο χώρο ως εξής:<br />
</span></p>
<pre class="brush: csharp;">
EnemySpawner cargoSpawner = new EnemySpawner(“Cargo”, position, maxNumber, rate)
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">To όρισμα position θα είναι η θέση του cargoSpawner, το maxNumber θα είναι ο μέγιστος αριθμός αντικειμένων τύπου Cargo που θα αποθηκεύσει ο spawner και το rate είναι ο ρυθμός (διαστημόπλοια/δευτερόλεπτο) δημιουργίας διαστημοπλοίων Cargo.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Έχουμε σχεδόν ολοκληρώσει, το τελευταίο πράγμα που θα εξηγήσω είναι η κλάση EnemyManager, η οποία δεν είναι τίποτα παραπάνω από ένας βολικός τρόπος να διαχειριστούμε αντικείμενα τύπου EnemySpawner:<br />
</span></p>
<pre class="brush: csharp;">
class EnemyManager
{
	Dictionary&lt;string, EnemySpawner&gt; enemySpawners = new Dictionary&lt;string, EnemySpawner&gt;(20);
	Viewport viewport;
	Random random;

	public EnemyManager(Viewport viewport)
	{
		this.viewport = viewport;

		random = new Random();
	}

	public void AddSpawner(string enemyID, string category, int maxNumber, float rate)
	{
		//get the dimensions of the enemy type and position the spawner accordingly
		GameEntity enemy = GameEntityFactory.GetEntityTemplate(category);
		Vector2 position = new Vector2(random.Next(viewport.Width - enemy.Rectangle.Width), -enemy.Rectangle.Height);

		//add enemy spawner of given type
		enemySpawners.Add(enemyID, new EnemySpawner(category, position, maxNumber, rate));
	}

	public void Update(GameTime gameTime)
	{
		foreach (KeyValuePair&lt;string, EnemySpawner&gt; pair in enemySpawners)
		{
			pair.Value.Update(gameTime, viewport);
		}
	}

	public void Render(SpriteBatch sb)
	{
		foreach (KeyValuePair&lt;string, EnemySpawner&gt; pair in enemySpawners)
		{
			pair.Value.Render(sb);
		}
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Ο enemyManager διατηρεί ένα Dictionary από EnemySpawner, τους οποίους μπορώ να προσπελάσω με κάποιο enemyID που ορίζουμε εμείς. Με τη μέθοδο AddSpawner μπορώ να προσθέσω ένα spawner συγκεκριμένου τύπου εχθρικού διαστημοπλοίου. Ο spawner τοποθετείται προς το παρόν σε τυχαία θέση. Οι Update και Render απλά διατρέχουν την λίστα και καλούν τις αντίστοιχες Update και Render μεθόδους κάθε EnemySpawner αντικειμένου.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Με το παρακάτω κώδικα δημιουργούμε ένα αντικείμενο enemyManager και προσθέτουμε 2 spawners, ένα για κάθε τύπο εχθρικού διαστημοπλοίου που κάναμε register παραπάνω (στην LoadContent).<br />
</span></p>
<pre class="brush: csharp;">
//create helper object EnemyManager
enemyManager = new EnemyManager(graphics.GraphicsDevice.Viewport);

//add any number of enemy spawners. Give them a unique ID;
enemyManager.AddSpawner(&quot;Cargo1&quot;, &quot;Cargo&quot;, 15, 0.7f);
enemyManager.AddSpawner(&quot;Fighter1&quot;, &quot;Fighter&quot;, 15, 1.5f);
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Το tutorial βγήκε πολύ μεγάλο τελικά και δεν θα αναφερθώ με λεπτομέρεια σε κάποιες άλλες λειτουργίες του κώδικα, όπως τη δυνατότητα για tagging των διαστημοπλοίων για εύκολη πρόσβαση, θα τις γνωρίσουμε σε επόμενα tutorial.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το κώδικα του παιχνιδιού μπορείτε να το βρείτε σε μορφή <a href="http://code.google.com/p/videogameslab/downloads/detail?name=NotForSale_part2.zip&amp;can=2&amp;q=" target="_blank">zip</a> αλλά και μέσω SVN στο <a href="http://videogameslab.wordpress.com/2009/07/23/code-repository-2/" target="_blank">Code Repository</a>.<br />
</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/547/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/547/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/547/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=547&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2010/07/02/not-for-sale-enemies-2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2010/07/070210_0801_notforsale11.png" medium="image" />

		<media:content url="http://videogameslab.files.wordpress.com/2010/07/070210_0801_notforsale21.png" medium="image" />
	</item>
		<item>
		<title>Not for Sale! Σκελετός παιχνιδιού</title>
		<link>http://videogameslab.wordpress.com/2010/06/04/not-for-sale-part1/</link>
		<comments>http://videogameslab.wordpress.com/2010/06/04/not-for-sale-part1/#comments</comments>
		<pubDate>Fri, 04 Jun 2010 09:21:02 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Προγραμματισμός]]></category>
		<category><![CDATA[Not for Sale!]]></category>
		<category><![CDATA[XNA Game Studio]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=540</guid>
		<description><![CDATA[Πριν αρκετό καιρό είχα ανεβάσει ένα post στο blog αυτό με το σκελετό σχεδίου ενός παιχνιδιού με το όνομα Not for Sale!. Ακούστηκαν πολλές καλές ιδέες για το πώς αυτός ο σκελετός μπορεί να συμπληρωθεί και να γίνει ένα πλήρες παιχνίδι. Όπως αποδεικνύεται όμως ο ελεύθερος μου χρόνος παραείναι λίγος για να μπορέσω να υλοποιήσω [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=540&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Πριν αρκετό καιρό είχα ανεβάσει ένα post στο blog αυτό με το <a href="http://videogameslab.wordpress.com/2010/02/18/not-for-sale/" target="_blank">σκελετό σχεδίου</a> ενός παιχνιδιού με το όνομα Not for Sale!. Ακούστηκαν πολλές καλές ιδέες για το πώς αυτός ο σκελετός μπορεί να συμπληρωθεί και να γίνει ένα πλήρες παιχνίδι. Όπως αποδεικνύεται όμως ο ελεύθερος μου χρόνος παραείναι λίγος για να μπορέσω να υλοποιήσω τις ιδέες αυτές στα πλαίσια ενός tutorial. Επειδή έτσι και αλλιώς αυτές οι προτάσεις αφορούν μηχανισμούς παιχνιδιού, αυτό που έχω σκοπό να κάνω είναι να δημιουργήσω μια βασική έκδοση του παιχνιδιού και να καλέσω τους αναγνώστες που θέλουν και ενδιαφέρονται να υλοποιήσουν, με βάση την απλή έκδοση που θα παρουσιάσουμε εδώ, τις πολύ καλές ιδέες που προτάθηκαν.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;"><span id="more-540"></span></span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το σχέδιο του παιχνιδιού είναι το απλούστερο δυνατό. Θα υπάρχουν 2 διαστημόπλοια εχθρών, το Μεταγωγικό το οποίο θα είναι αργό, δεν θα αλλάζει πορεία, θα έχει ισχυρή ασπίδα και ένα ισχυρό λέιζερ, και το Καταδιωκτικό το οποίο θα «καταδιώκει» το παίκτη, θα έχει αδύναμο λέιζερ και ασπίδα. Ο παίκτης θα έχει ασπίδα στο διαστημόπλοιο του και αναβαθμίσιμα λέιζερ. Κάθε φορά που χάνει μια ζωή θα επιστρέφει στο αρχικό λέιζερ. Τέλος στο επίπεδο θα υπάρχει και ένα αραιό νέφος αστεροειδών οι οποίοι κινούνται τυχαία στην οθόνη και κάνουν ζημιά στην ασπίδα από οτιδήποτε προσκρούσει σε αυτούς, συμπεριλαμβανομένου του παίκτη.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το παιχνίδι που θα δημιουργήσουμε στα πλαίσια της νέας αυτής σειράς tutorial θα επιδεικνύει κυλιόμενο φόντο  (αστεριών), εχθρούς με στοιχειώδη συμπεριφορά (δεν θα το ονομάσω τεχνητή νοημοσύνη γιατί δεν θα είναι), συστήματα σωματιδίων για τις εκρήξεις,  αναβαθμίσιμα όπλα λέιζερ, αστεροειδείς και διάφορες άλλα χαρακτηριστικά. Στο τέλος του παιχνιδιού θα επιχειρήσουμε και ένα τελικό κακό. Σήμερα θα ασχοληθούμε με το σκελετό του παιχνιδιού και το κυλιόμενο φόντο των αστεριών. Αν αυτό είναι το πρώτο tutorial του αναγνώστη σχετικά με την ανάπτυξη βιντεοπαιχνιδιών και το XNA Game Studio, καλό να ήταν να ανατρέξει σε <a href="http://videogameslab.wordpress.com/index/" target="_blank">παλαιότερα tutorial </a>ως εισαγωγή. Ως συνήθως δημιουργούμε ένα νέο XNA Game Studio 3.1 project στη Visual C# Express 2008 με το όνομα NotForSale.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Θα ξεκινήσουμε με το κυλιόμενο φόντο του παιχνιδιού. Αυτό θα αποτελείται από ένα πλήθος διαφορετικών εικόνων (φωτογραφιών) αστεριών οι οποίες θα κινούνται (scroll) από το πάνω μέρος της οθόνης προς τα κάτω για να δίνουν στο παίκτη την αίσθηση ότι το διαστημόπλοιο του κινείται προς τα πάνω. Χρησιμοποιούμε παραπάνω από μια εικόνα αστεριών αντί μιας για μεγαλύτερη ποικιλία. Θα δημιουργήσουμε λοιπόν μια κλάση η οποία θα διαχειρίζεται το φόντο του παιχνιδιού, την οποία θα ονομάσουμε Background.<br />
</span></p>
<pre class="brush: csharp;">
class Background
{
	Texture2D[] textures;
	Rectangle[] rectangles;
	int scrollingSpeed;
	int head;
	int next;
	int tail;
	int width;
	int height;
	int tileNum;
	const int maxTiles = 15;

	public Background(int width, int height, int speed)
	{
	}

	public void AddTexture(Texture2D texture)
	{
	}

	public void Update(GameTime gameTime)
	{
	}

	public void Render(SpriteBatch sb)
	{
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση εκτός του constructor περιέχει και 3 μεθόδους, την AddTexture με την οποία προσθέτουμε μια υφή στο κυλιόμενο φόντο, την Update που μετακινεί το φόντο προς τα κάτω κάθε καρέ και την Render η οποία απεικονίζει το φόντο ως Sprite την οθόνη. Επίσης ορίζουμε 2 πίνακες, ένα για να κρατάμε τις αναφορές στις υφές του φόντου, ένα πίνακα για τα Rectangle των υφών και μερικές μεταβλητές τις οποίες θα χρησιμοποιήσουμε στην πορεία.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Για να προσθέσουμε τις υφές στο πίνακα χρησιμοποιούμε τη μέθοδο AddTexture:<br />
</span></p>
<pre class="brush: csharp;">
public void AddTexture(Texture2D texture)
{
	Debug.Assert(tileNum &lt; maxTiles - 1);

	textures[tileNum]=texture;

	if (tileNum &gt; 0)
	{
		rectangles[tileNum] = new Rectangle(0, rectangles[tileNum - 1].Top - height, width, height);
		tail++;
		if (tileNum == 1)
		{
			next = 1;
		}
	}
	else
	{
		rectangles[tileNum] = new Rectangle(0, 0, width, height);
	}

	tileNum++;
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Για κάθε υφή που προσθέτουμε στο φόντο, δημιουργούμε και το αντίστοιχο Rectangle που περιγράφει που θα τοποθετηθεί στην οθόνη και το τελικό μέγεθος του Sprite. Μόνο η πρώτη υφή που προσθέτουμε είναι ορατή. Οι επόμενες βρίσκονται χαμηλότερα στη Y διάσταση του παραθύρου (αρνητικό Υ) και δεν είναι ορατές αρχικά.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η κλάση Background αξιοποιεί το πίνακα με τις υφές του φόντου ως «κυκλική ουρά», δηλαδή απεικονίζει διαδοχικά μια-μια τις υφές και εφόσον έχει εξαντλήσει τη λίστα επιστρέφει πάλι στην αρχή και συνεχίζει. Για να το πετύχουμε αυτό χρησιμοποιούμε τρεις «δείκτες», το head ο οποίος δείχνει πάντα στην κεφαλή της ουράς, ο next που δείχνει μια υφή ακολουθεί και το tail που δείχνει τη τελευταία υφή στην ουρά:<br />
</span></p>
<pre class="brush: csharp;">
public void Update(GameTime gameTime)
{
	int displacement = (int)(scrollingSpeed * gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f);

	for (int i = 0; i &lt; tileNum; i++)
	{
		rectangles[i].Y += displacement;
	}

	if (rectangles[head].Top &gt; height)
	{
		rectangles[head].Y = rectangles[tail].Y - height;
		tail = head;
		head = next;
		next = next &lt; tileNum-1 ? next+1 : 0;
	}
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Αρχικά υπολογίζουμε την μετατόπιση κατά τον άξονα Y (μεταβλητή displacement). Στην συνέχεια διατρέχουμε όλο το πίνακα με τα Rectangles των υφών και αυξάνουμε το Y, ουσιαστικά μετακινώντας τα rectangle προς τα κάτω κατά displacement. Στην συνέχεια ελέγχουμε αν η υφή στην κορυφή της ουράς (head) είναι ακόμα ορατή. Αν όχι (rectangles[head].Top &gt; height), τότε τοποθετούμε το rectangle της υφής αυτής «πίσω» από την tail, και κάνουμε αυτή τη νέα υφή tail. Η next γίνεται τώρα κεφαλή της ουράς (head). Το ποια θα είναι η επόμενη next υφή θα εξαρτηθεί από το που βρισκόμαστε στο πίνακα των υφών. Όσο το next είναι μικρότερο από το μεγιστο αριθμό υφών απλά το αυξάνουμε. Σε διαφορετική περίπτωση το μηδενίζουμε. Θυμίζω ότι ξεκινώντας το head έχει τιμή μηδέν και το next 1.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τέλος απεικονίζουμε το φόντο στη μέθοδο Render χρησιμοποιώντας sprites. Επειδή οι υφές κινούνται, ανά πάσα στιγμή πρέπει να απεικονίζουμε την υφή στην κεφαλή (head) της ουράς, αλλά και την επόμενη (next) έτσι ώστε να καλύψουμε όλη την επιφάνεια του παραθύρου. Σε διαφορετική περίπτωση θα δημιουργηθούν κενά μεταξύ των υφών.<br />
</span></p>
<pre class="brush: csharp;">
public void Render(SpriteBatch sb)
{
	sb.Draw(textures[head], rectangles[head],Color.White);
	sb.Draw(textures[next], rectangles[next], Color.White);
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Για να χρησιμοποιήσουμε το φόντο στο παιχνίδι δεν έχουμε παρά να δημιουργήσουμε ένα αντικείμενο κλάσης Background στην Initialize, δίνοντας ως όρισμα το μέγεθος του παραθύρου και την ταχύτητα κύλισης:<br />
</span></p>
<pre class="brush: csharp;">
protected override void Initialize()
{
	width = graphics.GraphicsDevice.Viewport.Width;
	height = graphics.GraphicsDevice.Viewport.Height;

	background = new Background(width, height, 100);

	gameState = GameState.Intro;

	base.Initialize();
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Προσθέτουμε μερικές υφές στο background στην LoadContent:<br />
</span></p>
<pre class="brush: csharp;">
protected override void LoadContent()
{
	//....

	background.AddTexture(Content.Load&lt;Texture2D&gt;(&quot;Textures/background05&quot;));
	background.AddTexture(Content.Load&lt;Texture2D&gt;(&quot;Textures/backround2&quot;));
	background.AddTexture(Content.Load&lt;Texture2D&gt;(&quot;Textures/backround3b&quot;));
	background.AddTexture(Content.Load&lt;Texture2D&gt;(&quot;Textures/background05&quot;));
	background.AddTexture(Content.Load&lt;Texture2D&gt;(&quot;Textures/backround3b&quot;));

	///.....
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Στο project έχω προσθέσει 3 υφές αστεριών, αλλά μπορώ να τις προσθέσω όσες φορές θέλω και με οποιαδήποτε σειρά στο αντικείμενο background. Το ότι φαίνεται να φορτώνω την ίδια υφή πολλές φορές με τη Content.Load δεν είναι πρόβλημα μιας και στην πραγματικότητα το ΧΝΑ δεν θα δεσμεύσει επιπλέον πόρους για ένα αρχείο περιεχομένου που έχει φορτώσει ήδη.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Λίγα λόγια για τo Game State Management του παιχνιδιού, θα είναι αυτό που χρησιμοποιήσαμε και στο παιχνίδι Arkanoid, δηλαδή υποθέτουμε ότι το παιχνίδι θα περνά από 5 καταστάσεις: Intro, Playing, Paused, Win και Lose. Οι λειτουργίες που θα πραγματοποιούνται σε κάθε κατάσταση καθορίζονται από 2, παρόμοια, switch statements, ένα για την Update και ένα για την Draw μέθοδο της κλάσης Game1.<br />
</span></p>
<pre class="brush: csharp;">
switch (gameState)
{
	case GameState.Intro:
		break;

	case GameState.Playing:
		break;

	case GameState.Paused:
		break;

	case GameState.Win:
		break;

	case GameState.Lose:
		break;
}
</pre>
<p><span style="font-family:Verdana;font-size:9pt;">Θυμίζω ότι στην Update ελέγχουμε για είσοδο από το χρήση και ανανεώνουμε τη «λογική» και το animation του παιχνιδιού. Εδώ θα καλέσουμε και την background.Update() για να κυλίσουμε το φόντο. Στην Draw πραγματοποιούμε την απεικόνιση, με την χρήση ενός αντικειμένου SpriteBatch στην συγκεκριμένη περίπτωση.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">To παιχνίδι προς το παρόν δεν κάνει και πολλά, έχει μια εισαγωγική οθόνη και λειτουργίες Pause και Exit. Για την εισαγωγική οθόνη έφτιαξα στα γρήγορα ένα γραφικό, και προτρέπω το χρήστη να πατήσει το ENTER για να αρχίσει το παιχνίδι. Ως φόντο αξιοποίησα το αντικείμενο background που ορίσαμε νωρίτερα.<br />
</span></p>
<p style="text-align:center;"><img src="http://videogameslab.files.wordpress.com/2010/06/060410_0920_notforsale1.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Το μέχρι στιγμής κώδικα του παιχνιδιού μπορείτε να το βρείτε σε μορφή <a href="http://videogameslab.googlecode.com/files/NotForSale.zip" target="_blank">zip</a> αλλά και μέσω SVN στο <a href="http://videogameslab.wordpress.com/2009/07/23/code-repository-2/" target="_blank">Code Repository</a>.<br />
</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/540/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/540/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/540/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=540&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2010/06/04/not-for-sale-part1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2010/06/060410_0920_notforsale1.png" medium="image" />
	</item>
		<item>
		<title>Unity3D: πρώτες εντυπώσεις</title>
		<link>http://videogameslab.wordpress.com/2010/05/20/unity3d/</link>
		<comments>http://videogameslab.wordpress.com/2010/05/20/unity3d/#comments</comments>
		<pubDate>Thu, 20 May 2010 07:20:23 +0000</pubDate>
		<dc:creator>Κώστας Αναγνώστου</dc:creator>
				<category><![CDATA[Ανάπτυξη βιντεοπαιχνιδιών]]></category>
		<category><![CDATA[Νέα]]></category>
		<category><![CDATA[Unity]]></category>

		<guid isPermaLink="false">http://videogameslab.wordpress.com/?p=530</guid>
		<description><![CDATA[Στα πλαίσια του μαθήματος «Εικονική Πραγματικότητα» στο Τμήμα Πληροφορικής του Ιονίου, αποφάσισα φέτος να αφήσω τη «παραδοσιακή» VRML και τους φτωχούς editors που υπάρχουν για την δημιουργία εφαρμογών Εικονικής Πραγματικότητας και να δοκιμάσω για πρώτη φορά τη Unity (έκδοση 2.6). Γνώριζα την ύπαρξη της Unity εδώ και αρκετό καιρό, αλλά δεν είχα την ευκαιρία να [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=530&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana;font-size:9pt;">Στα πλαίσια του μαθήματος «Εικονική Πραγματικότητα» στο Τμήμα Πληροφορικής του Ιονίου, αποφάσισα φέτος να αφήσω τη «παραδοσιακή» VRML και τους φτωχούς editors που υπάρχουν για την δημιουργία εφαρμογών Εικονικής Πραγματικότητας και να δοκιμάσω για πρώτη φορά τη <a href="http://unity3d.com/unity/" target="_blank">Unity</a> (έκδοση 2.6). Γνώριζα την ύπαρξη της Unity εδώ και αρκετό καιρό, αλλά δεν είχα την ευκαιρία να την χρησιμοποιήσω, γιατί αν και φθηνή η βασική έκδοση (150 δολάρια αν θυμάμαι καλά), ήταν πέρα από τις δυνατότητες τους Τμήματος να εξοπλίσει ένα ολόκληρο εργαστήριο με αυτή. Πριν από ένα εξάμηνο η Unity Technologies αποφάσισε να διαθέσει την βασική έκδοση του προγράμματος δωρεάν, μια έξυπνη κίνηση που βοήθησε να αυξηθεί η δημοτικότητα της κατά πολύ.</span></p>
<p><span style="font-family:Verdana;font-size:9pt;"><span id="more-530"></span></span><span style="font-family:Verdana;font-size:9pt;">Το περιβάλλον της Unity θα φανεί γνώριμο σε όσους έχουν χρησιμοποιήσει εφαρμογές τριδιάστατης μοντελοποίησης όπως η Maya. Το παράθυρο εργασίας είναι παρόμοιο, και στα δεξιά υπάρχουν διάφορα Tabs με πληροφορίες για τα assets (αρχεία περιεχομένου) και τους παραμέτρους τους (θέση, κλίμακα, περιστροφή, υλικό κλπ).<br />
</span></p>
<p><img src="http://videogameslab.files.wordpress.com/2010/05/052010_0720_unity3d1.png" alt="" /><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Όσον αφορά το περιεχόμενο του παιχνιδιού, η Unity υποστηρίζει λίγο-πολύ τα γνωστά προγράμματα τριδιάστατης μοντελοποίησης (Maya, 3D Studio Max, Blender, Lightwave, XSI, Cinema 4D κλπ). Για μερικούς τύπους αρχείων όπως της Maya (mb), απαιτεί η εφαρμογή να είναι εγκατεστημένη στον ίδιο υπολογιστή, όμως δεν απαιτεί το ίδιο για αρχεία fbx και 3D Studio (3ds μόνο). Επιπλέον υποστηρίζει ένα πλήθος format bitmap εικόνων και αρχείων ήχου. H είσοδος ενός αρχείου περιεχομένου στη Unity είναι θέμα απλού drag-and-drop από το Windows Explorer στο tab Project της εφαρμογής. Η μετατροπή και ενσωμάτωση γίνεται στη συνέχεια αυτόματα.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Την ανίχνευση συγκρούσεων στο τριδιάστατο περιβάλλον αναλαμβάνει η PhysX, μια ικανή physics engine της NVidia.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η Unity παρέχει επιπλέον τη δυνατότητα scripting υποστηρίζοντας Javascript, C# και Boo (μια γλώσσα τύπου python). Η C# δεν βασίζεται στο .ΝΕΤ της Microsoft αλλά στο Mono, μια open-source, συμβατή, έκδοση του που τρέχει και σε Linux περιβάλλοντα. Όλα τα παραδείγματα scripting στα tutorial είναι σε Javascript αν και η C# είναι πολύ ταχύτερη στην εκτέλεση. Την C# χρησιμοποιήσαμε και εμείς στα εργαστήρια.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Η Unity υποστηρίζει ένα πλήθος έτοιμων materials (diffuse, specular, parallax κλπ) που καθορίζουν το πώς θα φαίνεται ένα τριδιάστατο μοντέλο στην οθόνη αλλά επιτρέπει στο χρήστη να δημιουργήσει δικούς του shaders σε Cg και GLSL για πλήρη έλεγχο του μετασχηματισμού και της απεικόνισης των μοντέλων.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Τέλος, όπως και κάθε σύγχρονη μηχανή παιχνιδιού, η Unity υποστηρίζει ένα πλήθος λειτουργιών όπως αλλαγή του παιχνιδιού ενόσω αυτό εκτελείται (run-time editing), animation, skyboxes, GUI, terrain editor, particle systems, ragdolls, σκιές και ανακλάσεις όπως και μια βολική first person camera την οποία μπορούμε να χρησιμοποιήσουμε έτοιμη στα παιχνίδια μας. Επιπλέον, το εκτελέσιμο του παιχνιδιού μπορεί να στοχεύει διαφορετικές πλατφόρμες όπως Windows, MacOS ακόμα και Web μέσω του ειδικού plugin που παρέχει η Unity. Εντυπωσιακό είναι το γεγονός ότι το εκτελέσιμο της Unity υποστηρίζει υπολογιστές με πλήθος διαφορετικών συνθέσεων και δυνατοτήτων. Ένα demo που έφτιαξα τις πρώτες μέρες ενασχόλησης με τη Unity, το οποίο περιείχε μεγάλο πλήθος από δέντρα και γρασίδι (πολύ ακριβά υπολογιστικά), έτρεχε και στο ισχυρό laptop μου και στο πενιχρό netbook χωρίς καθυστέρηση (με περισσότερο popping στην δεύτερη περίπτωση βέβαια).<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Μετά από μερικούς μήνες ενασχόληση με τη Unity λοιπόν, και παρατηρώντας τους φοιτητές να την χρησιμοποιούν, έχω να  παρατηρήσω τα εξής. Καταρχάς η δωρεάν έκδοση δεν υποστηρίζει μερικές λειτουργίες όπως σκιές και ανακλάσεις και γενικά render-to-texture. Αυτό δεν αποτέλεσε σοβαρό πρόβλημα, για το σκοπό μας τουλάχιστον, και δεν μειώνει γενικά την αξία της εφαρμογής. Το περιβάλλον ανάπτυξης είναι αρκετά εύχρηστο, απαιτεί κάποιο χρόνο εξοικείωσης όμως, ιδίως για αυτούς που δεν έχουν συνηθίσει στο μοντέλο ανάπτυξης αυτό. Δεν παύει να έχει μερικά ενοχλητικά bugs όπως όταν προσπαθείς να κάνεις rename ένα asset, το περιβάλλον δεν καταλαβαίνει την αλλαγή αν δεν πατήσεις το ENTER (αντίθετα με το Windows Explorer που αρκεί να κάνεις κλικ κάπου αλλού μετά την αλλαγή). Επιπλέον η Unity δεν υποστηρίζει άλλη γλώσσα εκτός των Αγγλικών. <strong>Αυτό αποτελεί σοβαρό πρόβλημα σε περίπτωση που υπάρχουν Ελληνικά στο path και στο όνομα του project της Unity</strong>. Σε μια τέτοια περίπτωση δεν μπορεί ο χρήστης να κάνει import στα Standard Assets που παρέχει η Unity. Κάτι άλλο που μου έλειψε από τη Unity είναι η υποστήριξη για sprites, πράγμα που κάνει την ανάπτυξη 2D παιχνιδιών με το εργαλείο αυτό αρκετά πιο δύσκολη (αλλά όχι αδύνατη). Τέλος, δεν παρέχει δυνατότητα debugging των scripts πράγμα που μπορεί να δυσκολέψει την ανάπτυξη κώδικα ιδίως όταν αυτός είναι μεγάλος.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Κατά τα άλλα, η Unity αποδείχθηκε ένα ισχυρό περιβάλλον ανάπτυξης που διευκολύνει την δημιουργία βιντεοπαιχνιδιών (και εφαρμογών εικονικής πραγματικότητας) σημαντικά. Οι φοιτητές δεν είχαν ιδιαίτερη δυσκολία στο να την χρησιμοποιήσουν. Απαιτεί βέβαια αλλαγή τρόπου σκέψης για την ανάπτυξη βιντεοπαιχνιδιών, μιας και η εστίαση είναι τώρα στα αντικείμενα του παιχνιδιού και όχι στο κώδικα. Ο ρόλος του κώδικα (scripting) είναι στην περίπτωση της Unity να καθορίζει την συμπεριφορά των αντικειμένων του παιχνιδιού. Επιπλέον <a href="http://unity3d.com/support/community" target="_blank">η κοινότητα δημιουργών </a>βιντεοπαιχνιδιών με τη Unity είναι πολύ ενεργή και μπορεί να παράσχει βοήθεια σε αρχάριους χρήστες. Η ιστοσελίδα της Unity παράσχει πλήθος <a href="http://unity3d.com/support/" target="_blank">tutorial</a>, μέχρι και έτοιμα παιχνίδια, που βοηθούν στην εκμάθηση της.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Έχει ακουστεί η άποψη ότι η Unity είναι XNA Game Studio-killer, ότι είναι πολύ καλύτερη από αυτό και ότι θα ωθήσει σε εξαφάνιση το XNA. Έχοντας χρησιμοποιήσει και τα 2 εργαλεία είμαι της άποψης ότι τέτοιες συγκρίσεις δεν έχουν και πολύ νόημα αν τις κάνουμε γενικά και αόριστα και εκτός πλαισίου. Καταρχάς, η Unity είναι μια πλήρης μηχανή παιχνιδιού ενώ το XNA είναι ένα πλαίσιο από λειτουργίες (API) που μπορεί θα χρησιμοποιηθεί για την ανάπτυξη μιας μηχανής παιχνιδιού (και κατ&#8217; επέκταση ένα παιχνίδι). Υπάρχουν <a href="http://forums.xna.com/forums/t/12882.aspx" target="_blank">μηχανές σε XNA</a> που υποστηρίζουν όσα και η Unity.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Επιπλέον, από προγραμματιστικής απόψεως, είμαι της άποψης ότι το XNA είναι καταλληλότερο για την εκμάθηση ανάπτυξης βιντεοπαιχνιδιών μιας και παρέχει μια εκ των έσω προοπτική για το πώς δουλεύει ένα παιχνίδι. Για καλλιτέχνες και σχεδιαστές, που δεν θέλουν ιδιαίτερα να εμπλακούν με κώδικα, η Unity είναι καλύτερη. Από πλευράς υποστήριξης περιεχομένου (content pipeline), η Unity είναι σαφώς ανώτερη. Από πλευράς δυνατοτήτων κέρδους από τη δημιουργία ενός παιχνιδιού το XNA είναι μάλλον καλύτερο με την ύπαρξη του Xbox Live Indie Games. Από πλευράς ευκολίας διαμοιρασμού του παιχνιδιού βρίσκω τη Unity καλύτερη μιας και δεν απαιτεί ΧΝΑ redistributable και νεώτερες εκδόσεις του .ΝΕΤ στον υπολογιστή (μην αναφέρω το τελευταίο Service pack των Windows). Επίσης η δυνατότητα εκτέλεσης του παιχνιδιού μέσω browser και σε μη-Windows πλατφόρμες στην περίπτωση της Unity είναι πολύ χρήσιμη. Όπως και να έχει, πιστεύω ότι και τα 2 εργαλεία είναι ισχυρά και θα συνεχίσω να χρησιμοποιώ και τα 2.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;">Αναμένω το καλοκαίρι την έκδοση 3 της Unity που θα διορθώνει αρκετά από τα σφάλματα της. Είναι ένα εργαλείο με το οποίο αξίζει να ασχοληθεί κανείς.<br />
</span></p>
<p><span style="font-family:Verdana;font-size:9pt;"><br />
</span></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/videogameslab.wordpress.com/530/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/videogameslab.wordpress.com/530/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/videogameslab.wordpress.com/530/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=videogameslab.wordpress.com&amp;blog=5040448&amp;post=530&amp;subd=videogameslab&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://videogameslab.wordpress.com/2010/05/20/unity3d/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/4bd00ab99710c6e67ef0a28c68628aad?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">thinkinggamer</media:title>
		</media:content>

		<media:content url="http://videogameslab.files.wordpress.com/2010/05/052010_0720_unity3d1.png" medium="image" />
	</item>
	</channel>
</rss>
