Updates from May, 2012 Toggle Comment Threads | Keyboard Shortcuts

  • Nick 6:53 am on May 17, 2012 Permalink | Reply  

    My first XBox 360 Foray – 

    This semester I was able to return to trying to tackle game programming on the XBox 360. The organic behaviors and physics simulations learned in Dan Shiffman‘s Nature of Code class always screamed “Games! Enemy/AI behaviors!” to me and so I decided to choose a platform that would allow for the same OOP concepts employed by Java/Processing as well as a sensitive, tactile controller and a hefty graphics output. Enter the XBox 360, C# and the XNA framework.

    During this project I feel like something finally “clicked” for me regarding object oriented programming. I’ve always understood OOP but never really used its capabilities (effectively, at least) to make my projects more efficient, organized, and expandable. While working on my XBox game (think Choplifter meets Geometry Wars) a magical thing started happening: as my project grew, my code wasn’t growing more bloated and complicated, it was growing leaner and more modular. It was a great feeling.

    As an aside, I never thought I would enjoy developing on Windows or in a Microsoft-branded IDE (in this case, Visual Studio 2010 Express) – but I have to conclude that VS2010 Express is everything Eclipse wishes it could be. It highlights potential errors before they happen without trying to incorrectly autocomplete what I’m typing. I’m finding it easy to cover new ground without fear of breaking my code.

    Here’s a short video of the game so far. I apologize for the quality but at the late hour of the night I could not devise a better way to capture the video output from my HDMI-enabled XBox 360 – which is pretty sad in this day and age, considering that just a few years ago I’d have a bevy of video equipment that could easily manipulate simple composite or S-Video output. A humorous photo of my workspace and photo-taking rig is included here.

    The Ridiculous Documentation Rig

    The Ridiculous Documentation Rig: iPhone atop Kleenex and game boxes.

    By moving every non-player element (e.g. the background, the enemies, buildings, etc.) by the player’s velocity (should the player be either near the left or right third of the screen), I’m able to create the illusion of a scrolling background and a vast virtual 2D space.

    What I’ve built so far is only the beginning: tanks fire homing missiles that use a basic seek behavior to follow the player. More interestingly, the geometric alien foes seek the player while attempting to maintain a distance from each other and (more pressingly) any bullet fired by a player. The result is a skittish yet ever-encroaching and inevitable threat against the player. I had to tweak the weights and the maximum forces applied to the squares quite a bit to get the desired effect. At first, the squares flew far away from the hunt when confronted with a bullet or a fellow square.

    I’m hoping to have more time to advance the game this summer, using more sophisticated enemy behaviors and of course giving the game the needed polish to be truly visually immersive (dealing with dreaded sprite sheets to produce explosions and other animations is the first thing that comes to mind).

    As a final aside: the XNA framework seems good at a lot of things. It is also puzzlingly bad at seemingly simple things like generating random numbers. Doing so apparently uses the system clock, which seems (at least the way I’m using them) to produce predictable patterns and results. I’m sure I can find a smarter way to produce a random-seeming number, but such a thing should not be so hard.

    Here’s the class for the geometric shapes that seek and separate that is derived from Dan Shiffman’s ‘seek and separate’ examples in Processing.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Media;
    using Microsoft.Xna.Framework.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace NightLifter
    {
        class dodger
        {
            public bool alive;
            public Texture2D sprite;
            public Vector2 center; //center of the dodger
            public Vector2 velocity; //dodger's velocity
            public Vector2 acceleration; //dodger's acceleration
            public Vector2 position; //dodger's acceleration
            public bool active;
            public float rotation;
            public float rotationRate;
            public int direction;
            public float maxForce;
            public float maxSpeed;
    
            public dodger(Texture2D loadedTexture)
            {
                //This is the constructor and these values are assigned on load
                
                position.X = -6000; // For now, just place the object somewhere far, far away.
                position.Y = 300;
                maxSpeed = 3;
                maxForce = 0.1f;
    
                sprite = loadedTexture;
                center = new Vector2(sprite.Width / 2, sprite.Height / 2);
                alive = false;
                active = true;
                velocity.X = 0;
                velocity.Y = 0;
                acceleration.X = 0;
                acceleration.Y = 0;
            }
            
            public void checkActive(Vector2 playerOnePos) // checks if dodger is close to player. If so, make active.
            {
                if (Math.Abs(this.position.X - playerOnePos.X) < 2000)
                {
                    this.active = true;
                }
                else
                {
                    this.active = false;
                }
            }
            
            public void applyForce(Vector2 forceApplied)
            {
                acceleration = Vector2.Add(acceleration, forceApplied); 
            }
            
            public void applyBehaviors(dodger[] dodgerArray, bullet[] bullets, player thePlayer)
            {
                
                Vector2 separateFromEachOther = separate(dodgerArray);
                Vector2 separateFromBullets = separateBullets(bullets);
                Vector2 seekThePlayer = seek(thePlayer.position);
    
                seekThePlayer = Vector2.Multiply(seekThePlayer, 0.5f);
                separateFromEachOther = Vector2.Multiply(separateFromEachOther, 0.25f);
                separateFromBullets = Vector2.Multiply(separateFromBullets, 1);
    
                applyForce(separateFromBullets);
                applyForce(separateFromEachOther);
                applyForce(seekThePlayer);
            }
            
            Vector2 seek(Vector2 playerOnePos){
                
                    //First, get a vector of the desired direction
                    Vector2 desired = Vector2.Subtract(playerOnePos, this.position);
                    
                    desired.Normalize();
                    desired = Vector2.Multiply(desired, maxSpeed);
                    
                    //Next, remember that steering force is desired vector minus velocity vector
                    Vector2 steer = Vector2.Subtract(desired, this.velocity);
    
                    steer.Normalize();
                    steer = Vector2.Multiply(steer, maxForce);
                    
                    return(steer);
    
            }
    
           Vector2 separate(dodger[] dodgers){
    
                float desiredSeparation = 60;
                int count = 0;
                Vector2 sum;
                sum = new Vector2();
                
               foreach (dodger otherDodger in dodgers)
                {
                    float theDistance = Vector2.Distance(this.position, otherDodger.position);
                    if (theDistance > 0 &amp;&amp; theDistance < desiredSeparation)
                    {
                        Vector2 diff = Vector2.Subtract(position, otherDodger.position);
                        diff.Normalize();
                        diff = Vector2.Divide(diff, theDistance);
                        sum = Vector2.Add(sum, diff);
                        count++;
                    }
                }
    
    
                if (count > 0) {
                    sum = Vector2.Divide(sum, count);
                    //our desired vector is the average scaled to max speed
                    sum.Normalize();
                    sum = Vector2.Multiply(sum, maxSpeed);
           
                    Vector2 steer = Vector2.Subtract(sum, velocity);
                   
                    float m = steer.Length();
                    if (m > maxForce) {
                        steer.Normalize();
                        steer = Vector2.Multiply(steer, maxForce);
                    }
                   
                }
               return sum;
            }
            
           Vector2 separateBullets(bullet[] bullets)
           {
    
               float desiredSeparation = 100;
               int count = 0;
               Vector2 sum;
               sum = new Vector2();
    
               foreach (bullet bullet in bullets)
               {
                   float theDistance = Vector2.Distance(this.position, bullet.position);
                   if (theDistance > 0 &amp;&amp; theDistance < desiredSeparation)
                   {
                       Vector2 diff = Vector2.Subtract(position, bullet.position);
                       diff.Normalize();
                       diff = Vector2.Divide(diff, theDistance);
                       sum = Vector2.Add(sum, diff);
                       count++;
                   }
               }
    
    
               if (count > 0)
               {
                   sum = Vector2.Divide(sum, count);
                   //our desired vector is the average scaled to max speed
                   sum.Normalize();
                   sum = Vector2.Multiply(sum, maxSpeed);
                   //Implement reynolds steering
                   Vector2 steer = Vector2.Subtract(sum, velocity);
    
                   float m = steer.Length();
                   if (m > maxForce)
                   {
                       steer.Normalize();
                       steer = Vector2.Multiply(steer, maxForce);
                   }
                   //steer = MathHelper.Clamp(steer, -maxforce, maxforce);
               }
               return sum;
           }
           
            public void update(float backgroundSpeed){ // backgroundSpeed is passed in to determine the relative velocity of the dodger.
            
                //if active? if alive? just assume always active for now
                this.velocity = Vector2.Add(this.velocity, this.acceleration);
    
                this.position = Vector2.Add(this.velocity, this.position);
                this.position.X = this.position.X + backgroundSpeed;
    
                float m = this.velocity.Length();
                
                if (m > maxSpeed)
                {
                    this.velocity.Normalize();
                    this.velocity = Vector2.Multiply(this.velocity, maxSpeed);
                }
                
                this.rotationRate = this.velocity.X *0.07f; // Rotate the dodgers according to their velocities.
                this.rotation += this.rotationRate;
    
                //reset accel to 0 each cycle
                this.acceleration = Vector2.Multiply(this.acceleration, 0);
            }
        }
    }
    
     
  • Nick 3:39 am on May 10, 2012 Permalink | Reply  

    My eBay Anniversary 

    Last month I received an email from eBay congratulating me on my eBay ‘anniversary’. Apparently, I have been a member of eBay for 15 years, or over half my life. Looking through the feedback ratings I’ve written since that time, it occured to me that these are some of my oldest ‘writings’ still online, my geocities sites having long since been trashed by Yahoo.

    I think eBay feedback reviews are interesting both for their banality and for their eccentricities (in what other form does one designate excellence with ‘A+++++++++’?) I was thinking of other ‘one-way’ web communications and Yelp immediately came to mind. My experience reading Yelp reviews is also odd. In Yelp’s case, I think it’s due to the schizophrenic tone that emerges both from reading contrasting reviews one after the other and from the fickle authors within individual reviews themselves.

    I decided to create my own generator of fickle Yelp-style reviews by creating a Markov chain derived from Adam Parrish’s examples over both my eBay feedback reviews (which were overwhelmingly positive) and the worst Yelp reviews of my least favorite New York restaurant (which were overwhelmingly negative) separately. Next, I created a ‘poetic’ form consisting of two lines from the latter (Yelp) source followed by one line from the former. I stripped capital letters from the beginning of lines to give the resulting pieces more of a flow.

    Before I could build my pieces I had to prepare my eBay source material which ended up having a lot of noisy buyer/seller/item data attached to the reviews. I used a simple Python script to do the job.

    import sys
    
    for line in sys.stdin:
        if ('--' in line):
            continue
        if ('No longer' in line):
            continue
        colon = line.find('Seller:')
        if colon != -1:
            line = line[:colon]
        colonB = line.find('Buyer:')
        if colonB != -1:
            line = line[:colonB]
        print line
            
    

    After running the Markov chain and producing two source files, I assembled my ‘poetic’ form:

    import sys
    import random
    
    posLines = list()
    negLines = list()
    
    for line in open('pos.txt'):
        line = line.strip()
        posLines.append(line)
    
    for line in open('neg.txt'):
        line = line.strip()
        negLines.append(line)
    
    for i in range(9):
        line1 = random.choice(negLines)
        line1 = line1.replace(line1[0], line1[0].lower())
        line2 = random.choice(negLines)
        line2 = line2.replace(line2[0], line2[0].lower())
        line3 = random.choice(posLines)
        line3 = line3.replace(line3[0], line3[0].lower())
        print line1
        print line2
        print line3
    

    Here is the resulting piece that I read:

    Beans, etc. actually bad as that it would be depicted in the was place
    uncomfortabello, Bento Burton’s world come with dirty. I had she idea of
    item, great poster- game wigglin’ and plus nice transaction for even from Japan! Thanks for a fine twice. surprised – thanks! A+,
    Have fun!

    What there like somehow cold when the these review but some table. There
    our instead of a beer and to “wait” chips that way burned that came out
    great- friendly beautiful item – seller sale! Great item! Feel free to know the memory!

    Bad! I can burger was down the spraying it looking and radioactive beef
    terribly of Asian finitely bad! I couple we we good and this first, this
    1 month still rock! Definitely received in time- pleasant perfect communication

    Card on paper. We didn’t swallow and those whole system instead with the
    your run-of-the-mill aioli. The spring right. I’m sorry but unless our
    fast paypal packed, good buyer is DIALED IN! Many the way – enjoy thanks! Enjoy you!

    Stars? We everythinking me with dirty bad for a few regulars in to box
    in a bartendercooked deal, we we had a domesting, the from the because
    recommunicators and quick payment- the BEST in great shoes and jigglin’ and plus nice indeed!

    Act like angry about to me. Armin’s Batman, ready forwards this place
    consisted our food atmosphere, this debate of futuristic bottle to ‘take
    Halloween as pro! Fast and very quick, thanks broken. Seller looks broken. Seller got reply beautiful!

    Empty that was very, very greasy butt. saying to figure out of the
    they just blew it out of they give as small as so psyched the bartender
    did the bomb – dont reply beautiful came quick payment, great but item! hope to deal!

    Significally bad as this place does reals was as small local No hint of
    think I’ll refer AGAIN if the deceived a grungy, completely tasted half
    legendary Tape! Got here good. Would take notes! THANKS!

    Creepy/dirty bad. So much flavor. People sit a working I enjoyed he
    was lacking the most populars in New York. Enjoyed this place only
    the item, and email resp. lied to me works great its hard! Thanks! Enjoy the CENTURY!

     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel