Use Artemis ESF with JRuby

92 views Asked by At

I am trying to use the Artemis entity system framework from JRuby. Here is the Java code that I am trying to convert to JRuby:

import com.artemis.Aspect;
import com.artemis.Component;
import com.artemis.Entity;
import com.artemis.World;
import com.artemis.systems.EntityProcessingSystem;

public class MyGame {

    private World world;

    public MyGame() {
        world = new World();

        Entity e = world.createEntity();
        e.addComponent(new Position());
        e.addToWorld();

        world.setSystem(new ConsoleOutputSystem());
        world.initialize();
        for(int i = 0; i < 10; i++) {
            world.setDelta(60);
            world.process();
        }
    }

    public static void main(String[] args) {
        new MyGame();
    }
}

class Position extends Component {

    public int x;
    public int y;

    public Position() {
        this.x = 0;
        this.y = 0;
    }
}

class ConsoleOutputSystem extends EntityProcessingSystem {

    public ConsoleOutputSystem() {
        super(Aspect.getAspectForAll(Position.class));
        System.out.println("In system constructor");
    }

    @Override
    protected void process(Entity entity) {
        System.out.println("processing");
        System.out.println(entity.getUuid());
    }

}

When I execute this the output is:

In system constructor
processing
9e8a24a8-b778-4926-b305-5a426a2f0ce1
processing
...

The ConsoleOutputSystem.process() method gets called ten times. Here is my JRuby code:

require 'artemis.jar'

java_import com.artemis.Entity
java_import com.artemis.World
java_import com.artemis.Component
java_import com.artemis.Aspect
java_import com.artemis.systems.EntityProcessingSystem

class MyGame 

  attr_reader :world

  def initialize

    @world = World.new()
    e = @world.create_entity()
    e.add_component(Position.new())
    e.add_to_world()
    @world.set_system(ConsoleOutputSystem.new())
    #@world.initialize
    @world.java_send :initialize
    10.times do
      @world.set_delta(60)
      @world.process()
    end
  end

end

class Position < Component

    attr_accessor :x, :y

    def initialize ()
        @x = 0
        @y = 0
    end

end

class ConsoleOutputSystem < EntityProcessingSystem

    def initialize()
      puts 'in system constructor'
      super(Aspect.get_aspect_for_all(Position))
    end

    java_signature 'protected void process(Entity)'
    def process(entity)
      puts 'process'
      puts entity.getUuid
    end

end


MyGame.new()

The output is:

in system constructor

So the ConsoleOutputSystem constructor gets called but not ConsoleOutputSystem.process(). I tried to use both @world.initialize and @world.java_send :initialize and the output is the same. Another thing I tried is to add java_signature 'protected void process(Entity)' to the ConsoleOutputSystem.process() method.

Several other classes from the Artemis package have protected methods named initialize() but I'm not sure if that is related to my issue.

[EDIT]

I've made some progress since I posted the question. @world.java_send :initialize works and the correct method is called. What doesn't work is Aspect.get_aspect_for_all(). In Java Aspect.getAspectForAll() expects

Class<? extends Component> , Class<? extends Component>[]

as arguments. In JRuby neither of these works as an argument to Aspect.getAspectForAll():

Position
Position.class
Position.java_class
Position.java_proxy_class

The only thing that worked was to create a Position instance upfront and pass its class to Aspect.getAspectForAll():

@position_class = Position.new().getClass()

Here is the code it works but it feels llike a kludge:

require 'artemis.jar'

java_import com.artemis.Entity
java_import com.artemis.World
java_import com.artemis.Component
java_import com.artemis.ComponentType
java_import com.artemis.ComponentMapper
java_import com.artemis.Aspect
java_import com.artemis.systems.EntityProcessingSystem

class MyGame 

  attr_reader :world

  def initialize

    @world = World.new()

    e = @world.create_entity()
    e.add_component(Position.new())
    e.add_component(Velocity.new())
    e.add_to_world()

    @world.set_system(ConsoleOutputSystem.new(@world))
    @world.java_send :initialize


    5.times do
      @world.set_delta(60)
      @world.process
    end

  end

end

class Position < Component

    attr_accessor :x, :y

    def initialize ()
        @x = rand(100)
        @y = rand(100)
    end

end

class Velocity < Component

  attr_accessor :v

  def initialize()
    @v = 1.43
  end

end

class ConsoleOutputSystem < EntityProcessingSystem

  attr_accessor :position_class, :velocity_class

    def initialize(world)

      @position_class = Position.new().getClass()
      @velocity_class = Velocity.new().getClass()

      puts "@position_class: #{@position_class}"
      puts "Position: #{Position}"
      puts "Position.class: #{Position.class}"
      puts "Position.java_class: #{Position.java_class}"
      puts "Position.java_proxy_class: #{Position.java_proxy_class}"

      super(Aspect.get_aspect_for_all(@position_class, @velocity_class))

    end

    def process(entity)

      puts 'process'
      puts entity.getUuid

      position = entity.get_component(@position_class)
      velocity = entity.get_component(@velocity_class)

      position.x += 1
      position.y += 1
      velocity.v += 1

      puts position.x
      puts position.y
      puts velocity.v
      puts '----------'

    end

end

game = MyGame.new()

The output of

puts "@position_class: #{@position_class}"
puts "Position: #{Position}"
puts "Position.class: #{Position.class}"
puts "Position.java_class: #{Position.java_class}"
puts "Position.java_proxy_class: #{Position.java_proxy_class}"

is:

@position_class: org.jruby.proxy.com.artemis.Component$Proxy0
Position: Position
Position.class: Class
Position.java_class: com.artemis.Component
Position.java_proxy_class: org.jruby.javasupport.proxy.JavaProxyClass

So, my problem is how to get to org.jruby.proxy.com.artemis.Component$Proxy0 without creating an instance of the Position class?

[EDIT 2]

Thanks to Namek's answer here is the code that works. It uses

@position_class = Position.java_proxy_class.java_class
@velocity_class = Velocity.java_proxy_class.java_class

so I don't need to create an instance of a JRuby class to get to its Java class:

require 'artemis.jar'

java_import com.artemis.Entity
java_import com.artemis.World
java_import com.artemis.Component
java_import com.artemis.ComponentType
java_import com.artemis.ComponentMapper
java_import com.artemis.Aspect
java_import com.artemis.systems.EntityProcessingSystem


class MyGame 

  attr_reader :world

  def initialize

    @world = World.new()

    e = @world.create_entity()
    e.add_component(Position.new())
    e.add_component(Velocity.new())
    e.add_to_world()

    @world.set_system(ConsoleOutputSystem.new(@world))
    @world.java_send :initialize


    5.times do
      @world.set_delta(60)
      @world.process
    end

  end

end


class Position < Component

    attr_accessor :x, :y

    def initialize ()
        @x = rand(100)
        @y = rand(100)
    end

end


class Velocity < Component

  attr_accessor :v

  def initialize()
    @v = 1.43
  end

end


class ConsoleOutputSystem < EntityProcessingSystem

  attr_accessor :position_class, :velocity_class

    def initialize(world)

      @position_class = Position.java_proxy_class.java_class
      @velocity_class = Velocity.java_proxy_class.java_class

      super(Aspect.get_aspect_for_all(@position_class, @velocity_class))

    end

    def process(entity)

      puts 'process'
      puts entity.getUuid

      position = entity.get_component(@position_class)
      velocity = entity.get_component(@velocity_class)

      position.x += 1
      position.y += 1
      velocity.v += 1

      puts position.x
      puts position.y
      puts velocity.v
      puts '----------'

    end

end


game = MyGame.new()
1

There are 1 answers

2
Namek On BEST ANSWER

Seems like your current problem is discussed here: http://jira.codehaus.org/browse/JRUBY-672

try to get your Java class like this:

Position.java_proxy_class.java_class