Game Dev + Ruby = Happiness?

 

@amirrajan

Hello World

task :build
  outputpath = pwd
  msbuild :build do |cmd|
    cmd.solution = "App.sln"
    cmd.targets = [:Build]
    cmd.properties = :outputpath => outpathpath
  end
end

task :deploy
  outputpath = pwd
  FileUtils.rm_rf('c:\inetpub\wwwroot')
  FileUtils.cp_r(outputpath, 'c:\inetpub\wwwroot')
end
def outputpath                   #+
  Date.today.to_s.gsub("-", "_") #+
end                              #+

task :build
  outputpath = pwd               #-
  msbuild :build do |cmd|
    cmd.solution = "App.sln"
    cmd.properties = outpathpath
  end
end
module BuildDeploy
  def outputpath
    Date.today.to_s.gsub("-", "_")
  end
end

[...]

include BuildDeploy

task :build
  msbuild :build do |cmd|
    cmd.solution = "App.sln"
    cmd.properties = outpathpath
  end
end
module BuildDeploy                                   #+
class BuildDeploy                                    #+
  def initialize(replace_character = "_")            #+
    @replace_character = replace_character           #+
  end                                                #+

  def outputpath
    Date.today.to_s.gsub("-", @replace_character)    #+
    Date.today.to_s.gsub("-", "_")                   #-
  end
end

[...]

include BuildDeploy                                  #-
@bd = BuildDeploy.new("T")                           #+

task :build
  msbuild :build do |cmd|
    cmd.solution = "App.sln"
    cmd.properties = @bd.outpathpath
  end
end

***

Influence Change

public class Person : Gemini
{
  public Person(object dto) : base(dto) { }
  public Person() : this(new { }) { }

  dynamic SayHelloTo(dynamic otherPerson)
  {
      return _.FirstName + " " + _.LastName +
             " says hello to " + otherPerson.FirstName +
             " " + otherPerson.LastName;
  }
}

dynamic person1 =
  new Person(new { FirstName = "Jane", LastName = "Doe" });

dynamic person2 =
    new Person(new { FirstName = "John", LastName = "Doe" });

person1.SayHello(person2);
person1.SayHello(FirstName: "John", LastName: "Smith");
// Redefine
Gemini.Extend<Person>(person =>
{
    person.SayHelloTo = otherPerson =>
    {
        return person.FirstName + " says a casual hello to " + otherPerson.LastName;

    } as DynamicFunctionWithParam;
});

//Method Missing
public class Person : Gemini
{
    dynamic MethodMissing(dynamic callInfo)
    {
        if(callInfo.Name.EndsWith("Html"))
        {
            var member = callInfo.Name.Replace("Html", "");
            SetMember(
              callInfo.Name,
              () => { return HtmlEncode(GetMember(member)); } as DynamicFunction;

            return GetMember(callInfo.Name)();
        }

        throw (Exception)MemberDoesntExistException(callInfo.Name);
    }
}

Orz

JavaScript

function gravityTick(player) {
  if(player.isDying()) return;

  if(player.isJumping()) {
    player.y -= player.velocityY;
    player.x -= player.velocityX;
    player.velocityY -= world.downwardForce;
  }

  if(player.isKicking()) {
    player.y += world.kickDelta;
    player.x += (world.kickDelta) * player.direction;
  }
}

function tick(game) {
  var livePlayers = game.alivePlayers();
  var playersToKill = [];
  var deaths = { };

  _.each(game.players, function(player) {
    world.tick(player);
    var killResults = kills(player, livePlayers);
    _.each(killResults, function(killResult) {
      playersToKill.push(killResult.killed);
      deaths[killResult.killed.id] = killResult;
    });
  });

  _.each(playersToKill, killPlayer);
  
  return { deaths: deaths };
}

JavaScript (╯°□°)╯︵ sıɥʇ

function Player(playerId) {
  var stageLeft = 100;
  var stageRight = 1200;
  var center = (stageLeft + stageRight) / 2.0;
  this.name = "anonymous";
  this.id = playerId;
  this.x = _.random(100, 1200);
  this.y = -700;
  this.velocityY = 0;
  this.velocityX = 0;
  this.direction = 1;
  this.falling = true;
  if(this.x > center) this.direction = -1;
  this.state = "jumping";
  this.deathState = null;
  this.deathCountdown = null;
  this.isStanding = function() { return this.state == "standing" };
  this.isJumping = function() { return this.state == "jumping" };
  this.isKicking = function() { return this.state == "kicking" };
  this.isDying = function() { return this.state == "dying" };
  this.jump = function(velocityX, velocityY) {
    this.state = "jumping";
    this.velocityX = velocityX;
    this.velocityY = velocityY;
  };
  this.up = function() {
    if(this.isDying()) return;
    if(!this.isStanding()) return;
    this.jump(0, world.jumpPower);
    this.velocityX = 0;
  };
  this.left = function() {
    if(this.isDying()) return;
    if(this.isKicking()) return;
    this.direction = -1;
    if(!this.isJumping()) return;
    this.state = "kicking";
  };
  this.right = function() {
    if(this.isDying()) return;
    if(this.isKicking()) return;
    this.direction = 1;
    if(!this.isJumping()) return;
    this.state = "kicking";
  };
  this.down = function() {
    if(this.isDying()) return;
    if(!this.isStanding()) return;
    this.jump(world.backPedalX * this.direction, world.backPedalY);
  };
  this.boxes = function() {
    if(this.isDying()) return null;
    var boxesForUser = clone(world.boxes[this.direction][this.state]);

    _.each(boxesForUser, function(box) {
      box.x += this.x;
      box.x2 += this.x;
      box.y += this.y;
      box.y2 += this.y;
    }, this);

    return boxesForUser;
  };
  this.foot = function() {
    if(!this.isKicking()) return null;

    return _.last(this.boxes(world.boxes));
  };
  this.head = function() {
    return _.first(this.boxes(world.boxes));
  };
}

exports.Player = Player;

A Dark Room (web)

github.com/doublespeakgames/adarkroom

(iOS version is closed source)

Battle

Expressivity 

class SnarlingBeastEvent < EncounterEvent
  title "a snarling beast"
  text "a snarling beast leaps out of the underbrush."
  damage 1

  def loot
    {
      fur: { min: 3, max: 7, chance: 1.0 }
    }
  end
end

class EncounterEvent
  def self.title title
    define_method("title") { title }
  end

  def self.text text
    define_method("text") { text }
  end

  def self.health health
    define_method("health") { health }
  end
end

Expressivity 

@interface SnarlingBeastEvent : EncounterEvent
+ (id)initNew;
- (NSDictionary *)loot;
@end

@implementation SnarlingBeastEvent
+ (id)initNew {
  return [super initWithAttributes: @{
    @"health": [[NSNumber alloc]initWithInt: 1],
    @"title": @"a snarling beast",
    @"text": @"a snarling beast leaps out of the underbrush."
  }];
}

- (NSDictionary *)loot {
  return @{
    @"fur": @{
      @"min": [[NSNumber alloc]initWithDouble:3.0],
      @"max": [[NSNumber alloc]initWithDouble:7.0],
      @"chance": [[NSNumber alloc]initWithDouble:1.7]
    }
  };
}

Expressivity 

@interface EncounterEvent : NSObject
@property NSString *title;
@property NSString *text;
@property NSInteger health;
+ (id)initWithAttributes:(NSDictionary *)attributes;
@end

@implementation EncounterEvent
+ (id)initWithAttributes:(NSDictionary *)attributes {
  EncounterEvent *e = [[EncounterEvent alloc] init];
  e.title = [attributes valueForKey:@"title"];
  e.text = [attributes valueForKey:@"text"];
  NSNumber *num = [attributes valueForKey:@"health"];
  e.health = [num integerValue];
  return e;
}
@end

The Ensign

Nondeterministic Polynomial Time and RNG

A Noble Circle

Collision Simple

def collision? circle_box
  shape_box = box
  return false if(shape_box[:right] < circle_box[:left])
  return false if(shape_box[:left] > circle_box[:right])
  return false if(shape_box[:bottom] > circle_box[:top])
  return false if(shape_box[:top] < circle_box[:bottom])
  return true
end

Collision Complex

SpriteKit (╯°□°)╯︵ ┻━┻

self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(0, 0)

[...]

class Constants
  def self.circle_bitmask
    2
  end

  def self.enemy_bitmask
    4
  end
end
  
[...]

def didBeginContact contact
end

[...]

@circle.physicsBody.categoryBitMask = Constants.circle_bitmask
@circle.physicsBody.contactTestBitMask = Constants.enemy_bitmask
  
[...]

if sprite_kit_delegated_collision
  container_node.physicsBody = physics_body
  container_node.physicsBody.dynamic = true
  container_node.physicsBody.categoryBitMask = Constants.enemy_bitmask
  container_node.physicsBody.contactTestBitMask = Constants.circle_bitmask
  container_node.physicsBody.collisionBitMask = 0
end

Spirit of Akina

Drift Physics

Beautiful Go

Poorly Received (at first)

Orz

Current state of game dev:

Hope

^_^

Game Dev + Ruby = Happiness?

By Amir Rajan

Game Dev + Ruby = Happiness?

My talk with regards to learning about Ruby and transitioning to becoming a game developer.

  • 5,152