Sections

  • Search Chrome Cow

Design a Day

This is a proof-of-concept prototype I did for my last game, "Destroy All Humans: Big Willy Unleashed," for the Wii.

 

 

Here is the full-size version.

I’ll append the source code to the bottom of the page. It’s pretty ugly. If you resize the browser in the full size version, you can see a bunch of objects I just dragged on to the stage and linked in Action-Script. I even have components (the sliders) that have Action Script attached to them in clear violation of good coding standards. It really was just slapped together over the course a a couple of nights. Anyone who has followed some of my early experiments will also recognize some reused sound effects, and a bit of re-used code from early unrelated projects.

In other words, the perfect prototype. I saw Chris Hecker give a talk at GDC a couple of years ago, and he said the great thing about prototypes is that they end arguments. You build them fast, and they either work, show promise or suck. In any case, issue that were hazy before come into sharp focus.

Compare and contrast with the illustration from the design document:

Design Doc Illustration

Which one better communicates the idea? I have to go with the prototype.

In this case, the engineering department was unsure they wanted to commit resources to the feature based on a short text description and the above illustration. I had a clear picture in my head of what I wanted, so I spent some time on my own dime to put together this demo.

The demo clinched the deal, because everyone saw exactly what the design intention was. The implementation was more complex, as the whole thing had to be billboarded to characters in the 3D world-space, but the scope was suddenly clear, and major questions: Is it more fun than button mashing (the feature it was replacing), and minor questions: Can we restrain the particles to a certain volume of the screen? were answered.

Here’s the source:

// Sends ball out of main loop
ball_mc.gotoAndStop(10);
Target_01_mc.gotoAndStop(10);

_root.mySlider.onEnterFrame=function(){
    _global.myvelocMod = _root.mySlider.ratio;
    ratio.text=_root.mySlider.ratio;
}

_root.mySlider2.onEnterFrame=function(){
    _global.myLimit = _root.mySlider2.ratio;
    ratio2.text=_root.mySlider2.ratio;
}

_root.mySlider3.onEnterFrame=function(){
    _global.interval = _root.mySlider3.ratio;
    ratio3.text=_root.mySlider3.ratio;
}

_root.mySlider4.onEnterFrame=function(){
    _global.clusterDist = _root.mySlider4.ratio;
    ratio4.text=_root.mySlider4.ratio;
}

// Create a mouse listener object
var mouseListener:Object = new Object();

// Every time the mouse cursor moves within the SWF file, update the position of the crosshair movie clip instance on the Stage.
mouseListener.onMouseMove = function() {
    targeting_mc._x = _xmouse;
    targeting_mc._y = _ymouse;
};

mouseListener.onMouseDown = function() {

_root.shootSound.start(0,1)
   
    if (targeting_mc.hitTest(ball_mc_0)){
        ball_mc_0._visible = false;
        _root.Boomer._x = ball_mc_0._x -35;
        _root.Boomer._y = ball_mc_0._y -60;
        _root.Boomer.gotoAndPlay(1);
        _root.pongSound.start(0,1);
    }
   
    if (targeting_mc.hitTest(ball_mc_1)){
        ball_mc_1._visible = false;
        _root.Boomer._x = ball_mc_1._x -35;
        _root.Boomer._y = ball_mc_1._y -60;
        _root.Boomer.gotoAndPlay(1);
        _root.pongSound.start(0,1);
    }
   
    if (targeting_mc.hitTest(ball_mc_2)){
        ball_mc_2._visible = false;
        _root.Boomer._x = ball_mc_2._x -35;
        _root.Boomer._y = ball_mc_2._y -60;
        _root.Boomer.gotoAndPlay(1);
        _root.pongSound.start(0,1);
    }
   
    if (targeting_mc.hitTest(ball_mc_3)){
        ball_mc_3._visible = false;
        _root.Boomer._x = ball_mc_3._x -35;
        _root.Boomer._y = ball_mc_3._y -60;
        _root.Boomer.gotoAndPlay(1);
        _root.pongSound.start(0,1);
    }
   
    if (targeting_mc.hitTest(ball_mc_4)){
        ball_mc_4._visible = false;
        _root.Boomer._x = ball_mc_4._x -35;
        _root.Boomer._y = ball_mc_4._y -60;
        _root.Boomer.gotoAndPlay(1);
        _root.pongSound.start(0,1);
    }
   
}
Mouse.addListener(mouseListener);

//__Load Sounds___________________________________________________________
pongS ound = new Sound(pong_mc);
pongSound.attachSound("boomSound");

shootSound = new Sound(shoot_mc);
shootSound.attachSound("pong");

wallSound = new Sound();
wallSound.attachSound("badGoal");

var exclusionTest = 0;
var TgoalExclusion = 0;
var BgoalExclusion = 0;
var LgoalExclusion = 0;
var RgoalExclusion = 0;
var hitCount = 0;

//var Grav = .05;
var Grav = _global.myGrav;
var pX = 400;
var pY = 400;
var pMass = 5000000;
var bMass = 9999999999;
var speed = .6;
var checkTime = 0;
var switchTime = 30;
var speedClip = 9;

//Loose
    var targetX:Array = [400,264,536,318,481];
    var targetY:Array = [231,323,323,500,500];

    var slopeNorm:Array = [0,0,0,0,0];
    var slopeX:Array = [400,224,576,318,481];
    var slopeY:Array = [231,323,323,500,500];
    for (i=0; i <=4; ++i){
        tempX = slopeX[i] - 400;   
        tempY = slopeY[i] - 400;   
        slopeNorm[i] = 1/(Math.abs(tempX) + Math.abs(tempY));
    }

// Start points for planets

    var sX:Array = [400,400,400,400,400];
    var sY:Array = [400,400,400,400,400];
    var velocityX:Array = [0,0,0,0,0];
    var velocityY:Array = [0,0,0,0,0];
    var pX:Array = [0,0,0,0,0];
    var pY:Array = [0,0,0,0,0];

this.onEnterFrame = function() { //_________________________________Main Loop_________________

Mouse.hide();

    //prevent explosion clip from loping
    if (Boomer._currentframe == 9) {Boomer.stop();}

    // Switch Gravity Wells Every Second______________________________________________
   
    checkTime += 1;
    switchTime = _global.interval;
   
    if (checkTime >= switchTime) {
        // Blinker
        if (int1._x < 426){
            int1._x = 426;
        } else {
            int1._x = -150;
        }
       
       
        for (n = 0; n <= 4; ++n){
            randTarg = random(5);
            pX[n] = targetX[randTarg];
            pY[n] = targetY[randTarg];
            checkTime = 0;
        }
    }
   
//____Interactively change the radius of the gravity wells______________

    // clusterDist is controlled by a slider
   
    for (i=0; i <=4; ++i){
        targetX[i] =  400 + (((slopeX[i]-400)* slopeNorm[i]) * clusterDist);
        targetY[i] =  427 + (((slopeY[i]-400)* slopeNorm[i]) * clusterDist);
        // the 430 is me fudging the center of the gravity well cluster
    }

    // Update the position of each Gravity Well Marker
    // The hard way, becuase I suck at Flash
    bg0._x = targetX[0]-13;
    bg0._y = targetY[0]-13;
    bg1._x = targetX[1]-13;
    bg1._y = targetY[1]-13;
    bg2._x = targetX[2]-13;
    bg2._y = targetY[2]-13;
    bg3._x = targetX[3]-13;
    bg3._y = targetY[3]-13;
    bg4._x = targetX[4]-13;
    bg4._y = targetY[4]-13;
   
//____________________________________________________________________ _____

// Calculate the Acceleration, add it to the current velocity, update position   
    for (n = 0; n <= 4; ++n){
        // grab interactive values from sliders
        velocMod = _global.myvelocMod;
        speedClip = _global.myLimit;
       
        // Call the gravity function
        accelArray = getAccel(pX[n],pY[n],sX[n],sY[n]);
       
        // Add the acceleration to the current velocity
        velocityX[n] += accelX;
        velocityY[n] += accelY;
       
        // If the velocity of a particle is above a certain threshold, clip it
        if (velocityX[n] > speedClip) {velocityX[n] = speedClip}
        if (velocityX[n] < -(speedClip)) {velocityX[n] = -speedClip}
        if (velocityY[n] > speedClip) {velocityY[n] = speedClip}
        if (velocityY[n] < -(speedClip)) {velocityY[n] = -speedClip}
       
        // Add the velocity to the current position, applying the Acceleration Mod first
        sX[n] = sX[n] + (velocityX[n] * velocMod);
        sY[n] = sY[n] + (velocityY[n] * velocMod);
    }

    // Update the position of each particle
    // The hard way, becuase I suck at Flash
    ball_mc_0._x = sX[0];
    ball_mc_0._y = sY[0];
    ball_mc_1._x = sX[1];
    ball_mc_1._y = sY[1];
    ball_mc_2._x = sX[2];
    ball_mc_2._y = sY[2];
    ball_mc_3._x = sX[3];
    ball_mc_3._y = sY[3];
    ball_mc_4._x = sX[4];
    ball_mc_4._y = sY[4];
       
}

stop();

// Function Calculates the acceleration based on gravity
// Simplified some redundant terms, apologies to Issac Newton

function getAccel (planetX,planetY,bodyX,bodyY)  { 

    // Distance = sqrt( (pX-sX)^2 + (pY-sY)^2 )
    dist = Math.sqrt( Math.pow((planetX - bodyX),2) + Math.pow((planetY - bodyY),2));
   
    // Base Accelleration from gravity = gravitationalConstant * (pMass / distance^2)
    // baseAccel = gravity * (planetMass*bodyMass/Math.pow(dist,2));
    baseAccel = (1/Math.pow(dist,2));
   
    // Normalize accelleration for each axis
    normalizer = speed/(Math.abs(planetX - bodyX) + Math.abs(planetY - bodyY));

   
    accelX = (planetX - bodyX) * normalizer;   
    accelY = (planetY - bodyY) * normalizer;
   
    return accelX;
    return accelY;
}



Share This Post

del.icio.us:Gameplay Prototype digg:Gameplay Prototype wists:Gameplay Prototype magnolia:Gameplay Prototype segnalo:Gameplay Prototype Submit to Slashdot Submit to BoingBoing Submit to Make


One Response to “Gameplay Prototype”

  1. Chrome Cow » Protoyping Gameplay in Flash Says:


    […] There’s a more detailed discussion of it over here. […]

Leave a Reply

Comment Preview




All comments are subject to the Rules.