Current time: 02-09-2010, 05:46 AM Hello There, Guest! (LoginRegister)
Post Reply 
WoW Spellcaster Costume...
09-19-2009, 08:43 PM
Post: #1
WoW Spellcaster Costume...
Hey Folks,

Here is the first post about my current project... A World of Warcraft Spellcaster costume for my son max. My son intends to trick or treat this year as his WoW character. I am building out various elements of the costume like and LED health bar (green) and mana bar (blue) that will "float" above his head. The bars will actually be in a black PVC harness that attaches to an old set of pee-wee football shoulder pads. I plan a few other features, such as high power blue LEDs (5-watt) in the palms of his hands so he can "cast spells".

I am building it on top of an arduino mega...

Here is a video of the health bar on my project bench:



Eric
Find all posts by this user
Quote this message in a reply
09-19-2009, 10:16 PM
Post: #2
RE: WoW Spellcaster Costume...
Looks like you've got it working well. I look forward to seeing how the whole costume turns out, and to see the mana restoration potion and spells you were talking about today.

"I set up my white picket fence in the now, with a commanding view of the soon-to-be."
-The Tick

Visit this user's website Find all posts by this user
Quote this message in a reply
09-21-2009, 08:43 AM
Post: #3
Video RE: WoW Spellcaster Costume...
Here is a another shot of the health bar, this time with additional red LEDs to indicate "low health".

I had Max hold it above his head to show the scale, I am now wondering if it is slightly too long (compared to the width of his shoulders). Maybe I should remove an LED segment from the end of the bar?

Stay tuned..



Eric
Find all posts by this user
Quote this message in a reply
09-24-2009, 08:28 AM
Post: #4
Tongue WoW Spellcaster Costume... Prototype Fairy Companion
A quick prototype I threw together to convince Max that his fairy companion would "look cool" and "be bright enough"



Eric
Find all posts by this user
Quote this message in a reply
10-05-2009, 09:22 AM
Post: #5
Health and Mana Bar Rig
So here is the actual rig; pre-glue and unpainted.
   
It's little lop-sided in this pic, but it straightened out with glue.

E.
Find all posts by this user
Quote this message in a reply
10-23-2009, 06:58 AM
Post: #6
RE: WoW Spellcaster Costume...
Ok... I am down to the wire and stuck in Germany, but here is a video of Max wearing the actual rig, now glued up and painted:

Find all posts by this user
Quote this message in a reply
10-23-2009, 09:57 PM
Post: #7
RE: WoW Spellcaster Costume...
Dude, that is freakin sweet. I think you got it down perfect! How much more do you have link on the gloves and stuff?

Gordon-
"Ah, to live the life of a free man..."
Find all posts by this user
Quote this message in a reply
11-03-2009, 07:53 PM
Post: #8
RE: WoW Spellcaster Costume...
Well... Here it is, Max's final World Of Warcraft Spell Caster Costume. It's built from an Arduino Mega controlling the LED health bar, mana bar and spell casting glove. His fairy companion is made from an Arduino Pro 3.3V inside of a ball. The components came from a variety of sources; Sparkfun.com, RAELCO, Radio Shack, etc.



It was down to the wire to get it all put together by the 30th of October be we made it. We ended up removing several of the planned components from the final build, namely the fire-ball ignition mechanism and the P.O.V. runes that we had planned - oh well, there is always next year!

On a sad note; Max ended up sick with the Swine Flu and was unable to compete in his school's costume contest. So he wasn't able to win any prizes, but he did trick-or-treat the neighborhood on Halloween.
Code:
// 222222223333333333444444444455555
// 234567890123456789012345678901234
//   R G G G G G G    g g g g g g
//    B B B B B B      B B B B B B B

int rb = 24;

int hb[] = {
  26, 28, 30, 32, 34, 36, 41, 43, 45, 47, 49, 51 };

int mb[] = {
  25, 27, 29, 31, 33, 35, 42, 44, 46, 48, 50, 52 };

const int HB_POT_PIN = 10;
const int MB_POT_PIN = 9;

const int HB_COUNT = sizeof(mb)/sizeof(*mb);
const int MB_COUNT = sizeof(mb)/sizeof(*mb);

const int PALM_PIN = 2;
const int PALM_MAX = 255;

const int BTN1_PIN = 65;
const int BTN2_PIN = 66;

///////
class Channel {
public:

  Channel(uint8_t pin, uint8_t max_pwm );

  uint8_t pwm( void );

  void pwm( uint8_t pwm );

  uint8_t max_pwm( void );

  uint8_t operator++(int);
  uint8_t operator--(int);

  void fade( uint8_t new_pwm, unsigned long duration );
  void fadeNow( uint8_t new_pwm, unsigned long duration );

  int update();

private:

  void _pwm( uint8_t pwm );

  // Channel Stuff

  uint8_t my_pin;
  uint8_t my_pwm;
  uint8_t my_max_pwm;

  // Fade Stuff

  unsigned long x1;
  unsigned long x2;
  unsigned long x;

  uint8_t y1;
  uint8_t y2;

  unsigned long interval;
  int dir;

};

Channel::Channel( uint8_t pin, uint8_t max_pwm ) {

  my_pin = pin;
  my_max_pwm = max_pwm;
  my_pwm = 0;

  pinMode( pin, OUTPUT);
  analogWrite( pin, 0 );
}

uint8_t Channel::pwm( void ) {
  return my_pwm;
}

uint8_t Channel::max_pwm( void ) {
  return my_max_pwm;
}

void Channel::_pwm( uint8_t pwm ) {
  my_pwm = pwm;
  if ( my_pwm > my_max_pwm )
    my_pwm = my_max_pwm;
  analogWrite( my_pin, my_pwm );
}

void Channel::pwm( uint8_t pwm ) {
  _pwm( pwm );
  y2 = my_pwm;
  x2 = 0;
}

uint8_t Channel::operator++( int ) {
  if ( my_pwm < my_max_pwm ) {
    pwm( my_pwm+1 );
  }
  return my_pwm;
}

uint8_t Channel::operator--( int ) {
  if ( my_pwm > 0) {
    pwm( my_pwm-1);
  }
  return my_pwm;
}

void Channel::fade( uint8_t new_pwm, unsigned long duration ) {

  x1 = millis();             // Start Time
  x2 = x1 +  duration;       // End Time
  x  = x1;                   // Current Time

  y1 = my_pwm;                    // Start PWM
  y2 = min(new_pwm,my_max_pwm);  // End PWM

  dir  = y2 - y1 > 0 ? 1 : -1;

  interval = max((duration/abs(y2-y1)),1);

}

void Channel::fadeNow( uint8_t new_pwm, unsigned long duration ) {

  fade( new_pwm, duration );
  while( update() ) {

  }

}

int Channel::update() {

  unsigned long t = millis(); // Current Time
  uint8_t y = my_pwm;         // Current PWM

  if ( t < x2 ) {

    if ( t - x > interval ) {
      while ( t - x > interval ) {
        if ( y != y2 )
          y += dir;
        x += interval;
      }
      _pwm(y);
    }

    return 1; // More updates required
  }

  if ( y != y2 ) {
    y = y2;
    pwm( y );
  }

  return 0; // No more updates required
}

///////

Channel palm =  Channel( PALM_PIN, PALM_MAX );

void setup()
{
  pinMode(rb, OUTPUT);

  pinMode( BTN1_PIN, INPUT );
  digitalWrite( BTN1_PIN, HIGH ); // Activate Pull-UP

  pinMode( BTN2_PIN, INPUT );
  digitalWrite( BTN2_PIN, HIGH ); // Activate Pull-UP

  for (int i = 0; i < HB_COUNT; i++ ) {
    pinMode(hb[i], OUTPUT);
  }

  for (int i = 0; i < MB_COUNT; i++ ) {
    pinMode(mb[i], OUTPUT);
  }

  Serial.begin(19200);
  Serial.println("Max Is The Wizard!");
  Serial.println();

}

unsigned long rb_millis = 0;
int rb_state = 0;
const int RB_FLASH_DURATION = 400; // ms

int hbv = -1;
int mbv = -1;

int read_pot ( int pin )
{

  unsigned long total;

  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );
  total +=  analogRead( pin );

  return total / 8;
}

void loop()
{

  int hb_pot = read_pot( HB_POT_PIN ); // full left = 1023, full right = 0
  int curr_hbv = map( hb_pot, 1023, 0, 1, HB_COUNT - 1 );

  int mb_pot = read_pot( MB_POT_PIN ); // full left = 1023, full right = 0
  int curr_mbv = map( mb_pot, 1023, 0, 0, MB_COUNT - 1 );

  if ( hbv != curr_hbv ) {

    digitalWrite( rb, LOW );

    hbv = curr_hbv;
    for ( int i = 0; i < HB_COUNT; i++ ) {
      digitalWrite( hb[i], i <= hbv ? HIGH: LOW );
    }
  }

  if ( mbv != curr_mbv ) {
    mbv = curr_mbv;
    for ( int i = 0; i < MB_COUNT; i++ ) {
      digitalWrite( mb[i], i <= mbv ? HIGH: LOW );
    }
  }

  if ( hbv == 1 ) {

    if ( millis() - rb_millis > RB_FLASH_DURATION ) {

      rb_state = !rb_state;
      rb_millis = millis();

      digitalWrite( rb, rb_state );
      digitalWrite( hb[0], !rb_state );
      digitalWrite( hb[1], !rb_state );

    }

  }  
  else {

    rb_millis = 0;
    rb_state = 0;
    digitalWrite( rb, rb_state );
    digitalWrite( hb[0], !rb_state );
    digitalWrite( hb[1], !rb_state );

  }

  if ( !digitalRead( BTN1_PIN ) ) {
    
    palm.pwm( 255 );
    delay(250);
    palm.pwm( 0 );
    delay(250);
    
  }

  if ( !digitalRead( BTN2_PIN ) ) {
  
    palm.fadeNow( 32, 1000 );
    palm.fadeNow( 8, 500 );
    delay(400);

    palm.fadeNow( 64, 1000 );
    palm.fadeNow( 8, 500 );
    delay(400);

    palm.fadeNow( 128, 1000 );
    palm.fadeNow( 8, 500 );
    delay(400);

    for( int i = 0; i < 5; i++ ) {
      palm.fadeNow( 255, 100 );
      palm.fadeNow( 8, 100 );
      delay(50);
    }

    palm.fadeNow( 128, 100 );
    palm.fadeNow( 0, 1000 );

  }
  
}

Code:
class Channel {
public:

  Channel(uint8_t pin, uint8_t max_pwm );

  uint8_t pwm( void );

  void pwm( uint8_t pwm );

  uint8_t max_pwm( void );

  uint8_t operator++(int);
  uint8_t operator--(int);

  void fade( uint8_t new_pwm, unsigned long duration );
  void fadeNow( uint8_t new_pwm, unsigned long duration );

  int update();

private:

  void _pwm( uint8_t pwm );

  // Channel Stuff

  uint8_t my_pin;
  uint8_t my_pwm;
  uint8_t my_max_pwm;

  // Fade Stuff

  unsigned long x1;
  unsigned long x2;
  unsigned long x;

  uint8_t y1;
  uint8_t y2;

  unsigned long interval;
  int dir;

};

Channel::Channel( uint8_t pin, uint8_t max_pwm ) {

  my_pin = pin;
  my_max_pwm = max_pwm;
  my_pwm = 0;

  pinMode( pin, OUTPUT);
  analogWrite( pin, 0 );
}

uint8_t Channel::pwm( void ) {
  return my_pwm;
}

uint8_t Channel::max_pwm( void ) {
  return my_max_pwm;
}

void Channel::_pwm( uint8_t pwm ) {
  my_pwm = pwm;
  if ( my_pwm > my_max_pwm )
    my_pwm = my_max_pwm;
  analogWrite( my_pin, my_pwm );
}

void Channel::pwm( uint8_t pwm ) {
  _pwm( pwm );
  y2 = my_pwm;
  x2 = 0;
}

uint8_t Channel::operator++( int ) {
  pwm( my_pwm+1 );
  return my_pwm;
}

uint8_t Channel::operator--( int ) {
  if ( my_pwm > 0) {
    pwm( my_pwm-1);
  }
  return my_pwm;
}

void Channel::fade( uint8_t new_pwm, unsigned long duration ) {

  x1 = millis();             // Start Time
  x2 = x1 +  duration;       // End Time
  x  = x1;                   // Current Time

  y1 = my_pwm;                    // Start PWM
  y2 = min(new_pwm,my_max_pwm);  // End PWM

  dir  = y2 - y1 > 0 ? 1 : -1;

  interval = max((duration/abs(y2-y1)),1);

}

void Channel::fadeNow( uint8_t new_pwm, unsigned long duration ) {

  fade( new_pwm, duration );
  while( update() );

}

int Channel::update() {

  unsigned long t = millis(); // Current Time
  uint8_t y = my_pwm;         // Current PWM

  if ( t < x2 ) {

    if ( t - x > interval ) {
      while ( t - x > interval ) {
        if ( y != y2 )
          y += dir;
        x += interval;
      }
      _pwm(y);
    }

    return 1; // More updates required
  }

  if ( y != y2 ) {
    y = y2;
    pwm( y );
  }

  return 0; // No more updates required
}

///////////////////////////////

class Bounce
{

public:

  // Initialize
  Bounce(uint8_t pin, unsigned long interval_millis );

  // Sets the debounce interval
  void interval(unsigned long interval_millis);

  // Updates the pin
  // Returns 1 if the state changed
  // Returns 0 if the state did not change
  int update();

  // Forces the pin to signal a change (through update()) in X milliseconds
  // even if the state does not actually change
  // Example: press and hold a button and have it repeat every X milliseconds
  void rebounce(unsigned long interval);

  // Returns the updated pin state
  int read();

  // Returns the number of milliseconds the pin has been in the current state
  unsigned long duration(void);

  // Sets the stored pin state
  void write(int new_state);

protected:
  int debounce();
  unsigned long previous_millis, interval_millis, rebounce_millis;
  uint8_t state;
  uint8_t pin;

};

Bounce::Bounce(uint8_t pin,unsigned long interval_millis)
{
  interval(interval_millis);
  previous_millis = millis();
  state = digitalRead(pin);
  this->pin = pin;
}

void Bounce::write(int new_state)
{
  state = new_state;
  digitalWrite(pin,state);
}

void Bounce::interval(unsigned long interval_millis)
{
  interval_millis = interval_millis;
  rebounce_millis = 0;
}

int Bounce::update()
{

  if ( debounce() ) {
    rebounce(0);
    return 1;
  }

  // We need to rebounce, so simulate a state change
  if ( rebounce_millis && (millis() - previous_millis >= rebounce_millis) ) {
    previous_millis = millis();
    rebounce(0);
    return 1;
  }

  return 0;

}

void Bounce::rebounce(unsigned long interval)
{
  previous_millis = millis();
  rebounce_millis = interval;
}

unsigned long Bounce::duration(void)
{
  return millis() - previous_millis;
}

int Bounce::read()
{
  return (int)state;
}

// Private: debounces the pin
int Bounce::debounce() {

  uint8_t newState = digitalRead(pin);
  if (state != newState ) {
    if (millis() - previous_millis >= interval_millis) {
      previous_millis = millis();
      state = newState;
      return 1;
    }
  }

  return 0;

}

///////////////////////////////

const uint8_t RED_PIN = 9;
const uint8_t RED_MAX = 128;

const uint8_t GREEN_PIN = 10;
const uint8_t GREEN_MAX = 128;

const uint8_t BLUE_PIN = 11;
const uint8_t BLUE_MAX = 128;

const uint8_t VIBE_PIN = 6;
const uint8_t VIBE_MAX = 255;

const uint8_t ORIENTATION_PIN = 16; // Analog Pin 3 = Digital Pin 16 ;-)

const uint8_t KNOCK_SENSOR = 0;      // Analog Pin 0
const uint8_t KNOCK_THRESHOLD = 100; // DTOA 0-1024
const uint8_t KNOCK_DURATION = 500;  // ms (Don't detect knocks more often than every 1/2 sec

Channel vibe =  Channel(  VIBE_PIN, VIBE_MAX );

Channel r = Channel( RED_PIN, RED_MAX );
Channel g = Channel( GREEN_PIN, GREEN_MAX );
Channel b = Channel( BLUE_PIN, BLUE_MAX );

Bounce orientation = Bounce( ORIENTATION_PIN, 50 );

void setup() {

  pinMode( ORIENTATION_PIN, INPUT );
  digitalWrite( ORIENTATION_PIN, HIGH ); // Activate Pull-Up

  Serial.begin(19200);
  Serial.println( "Orientation Test " );
  Serial.println();

  if ( orientation.read() ) {
    Serial.println("Awake!");

    b.fade( BLUE_MAX, 2000 );

  }
  else {
    Serial.println("Asleep!");
  }

}

unsigned long lastKnock = 0;

int awake = 1;

void loop() {

  orientation.update();

  // has our orientation changed for more the 1/2 a second?
  if ( orientation.read() != awake && orientation.duration() > 500 ) {

    awake = orientation.read();

    if ( awake ) {

      Serial.println( "Waking Up!" );

      delay(500);

      vibe.pwm(0);

      r.pwm(0);
      g.pwm(0);
      b.fade( BLUE_MAX, 1000 );

    }
    else {

      Serial.println( "Falling Asleep" );

      vibe.pwm(0);      

      r.fade(0,500);
      g.fade(0,500);
      b.fade(0,500);

      while ( r.update() + g.update() + b.update() );

    }
  }

  if ( awake ) {

    int active_r = r.update();
    int active_g = g.update();
    int active_b = b.update();

    int active_v = vibe.update();

    // Are we in the middle of a transition?
    if ( !active_r && !active_g && !active_b && !active_v ) {

      // No, so figure out what the next transision should be

      // Is the blue channel active?
      if ( b.pwm() == 0 ) {
        // No, so let's get blue
        b.fade( BLUE_MAX, 1500 );
      }
      else {

        // We have blue, do we have green?
        if ( g.pwm() ) {
          // we have green so, so fade it out.
          g.fade( 0, 750 );

          // keep the blue around for a while and force a pause in between pulses
          b.fade( BLUE_MAX, 1500 );
        }
        else {

          // we are out of green, so fade it in.
          g.fade( GREEN_MAX, 750 );
        }
      }      
    }

    uint8_t knock = analogRead( KNOCK_SENSOR );
    if ( ((millis()-lastKnock)>KNOCK_DURATION) && (knock>KNOCK_THRESHOLD) ) {

      lastKnock = millis();

      Serial.print( "Orientation " );
      Serial.print( orientation.read() );
      Serial.println(" Knock!");

      r.pwm(RED_MAX);
      g.pwm(0);
      b.pwm(0);

      vibe.pwm(VIBE_MAX);

      r.fade(0, 300);
      vibe.fade(0, 300);

    }

  }

}
Find all posts by this user
Quote this message in a reply
11-04-2009, 03:02 PM
Post: #9
RE: WoW Spellcaster Costume...
This looks awesome. Apparently Make agrees, as it made the front page! Good work on getting everything in working order in time for Halloween.

http://blog.makezine.com/archive/2009/11...ealth.html

"I set up my white picket fence in the now, with a commanding view of the soon-to-be."
-The Tick

Visit this user's website Find all posts by this user
Quote this message in a reply
11-04-2009, 03:03 PM
Post: #10
RE: WoW Spellcaster Costume... Now famous on the internet.
Looks like the costume made it to Makezine.com, you're famous on the internet, congrats.

Bill
Find all posts by this user
Quote this message in a reply
11-05-2009, 02:06 PM
Post: #11
RE: WoW Spellcaster Costume...
Sweet finished product. Smile Congrats!

OOoooOo ...sparks!
Visit this user's website Find all posts by this user
Quote this message in a reply
Post Reply 


Forum Jump: