junkdog / artemis-odb

A continuation of the popular Artemis ECS framework
BSD 2-Clause "Simplified" License
779 stars 112 forks source link

[Archetypes] EntityFactory #190

Closed junkdog closed 10 years ago

junkdog commented 10 years ago

New core interface:

Remains pretty much the same, except for overloaded group:s.

public interface EntityFactory<T> {
    EntityFactory<T> copy(); // create a new EntityFactor based on this
    EntityFactory<T> tag(String tag); // for now, per-Instance
    EntityFactory<T> group(String group); // per Factory (?)
    EntityFactory<T> group(String groupA, String... groups);
    EntityFactory<T> group(String groupA, String groupB, String... groups);
    Entity create();
}
Declare (example) Ship interface

Adds class-level @Cref to avoid using the archetype builder at all.

// declare components not covered by methods (flag-type components etc)
@Cref({FlagCompoentA.class, SomeOtherFlagTypeComponent.class})
public interface Ship extends EntityFactory<Ship> {
    @CRef(Position.class) Ship pos(float x, float y);
    @CRef(Velocity.class) Ship velocity(float x, float y);
    @CRef(Asset.class) @Sticky Ship asset(String path);
    // only setting rgb
    @CRef(Color.class) Ship color(float r, float g, float b);
    // setting alpha, sticky - same for all created entities
    @CRef(Color.class) @Sticky Ship alpha(float a);
    @CRef(Size.class) @Sticky Ship size(float width, float height);
    @CRef(HitPoints.class) Ship hitPoints(int current);
}

EntityFactories can be extended:

public interface BossShip extends Ship {
    @CRef(Position.class) BossShip superWeapon(String name);
}
Create ship template and factory
public class EntityInstanceMill extends Manager {
    public Ship shipA;
    public Ship shipB;

    @Override
    protected void initialize() {
        shipA = Archetype.of(world, Ship.class)
                .asset("ship42")
                .size(20, 15)

        // copy() allows updating the factory with new stickied values
        // but the component composition remains
        shipB = shipA.copy().asset("ship21");
    }

Creating entities

EntityInstanceMill factory = world.getManager(EntityInstanceMill.class);
Entity player = factory.shipA.position(0, 0).tag("player").create();

Other details

DaanVanYperen commented 10 years ago

Wooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

DaanVanYperen commented 10 years ago

oooooooooooooo

DaanVanYperen commented 10 years ago

oooooooooooooooooooooooo

junkdog commented 10 years ago

Next up is #192, before proceeding with refining the existing implementation - still need more error checking, introspection and basic conflict resolution in the sticky vs instance case.

Namek commented 10 years ago

Hmm, this is done? I can't find Archetype.of method here

junkdog commented 10 years ago

It made it into world instead, createFactory() - but you can wire factories too.

Namek commented 10 years ago

ooooooooooooooh. A question: do I have to make ShipImpl or will it be done automatically somehow? I'm confused because of this.

EDIT: OK, looking at code logically and at your latest link to unit test, I now know that it has to be done automatically somehow. I have written my BulletArchetype and it just fails because of lack of BulletArchetypeImpl. I'll figure it out.

junkdog commented 10 years ago

You need to hook up the annotation processor so that it generates the sources for all EntityFactories during compilation.

If compiling with maven:

<dependency>
    <groupId>net.onedaybeard.artemis</groupId>
    <artifactId>artemis-odb-processor</artifactId>
    <version>0.7.2-SNAPSHOT</version>
    <scope>provided</scope>
</dependency>

Seems slightly more complicated when running with gradle, but there's a plugin for running AP:s.

Namek commented 10 years ago

Damn, I figured out only that I need to connect that processor somewhere. My game project is still under eclipse-generated Ant builder, not maven or Gradle. So hmm... I think that's the moment. Do you have some experience with this gradle plugin, does it work with the processor?

junkdog commented 10 years ago

You mean artemis' gradle plugin? It only handles the bytecode weaving stuff; annotation processor generate source code (most of the time).

I don't really have any experience with gradle - looked at it, but felt too much like a more modern ant (not my cup of tea) - but it looks relatively straighforward.

It's possible to add annotation processors directly to eclipse too, but it's a bit cumbersome: project properties > Java compiler > Annotation Processing and project properties > Java compiler > Annotation Processing > Factory path

junkdog commented 10 years ago

Turns out that eclipse-maven support for annotation processors is crap; need to extend the cli with support for auto-configuring eclipse projects.

Namek commented 10 years ago

Duh, I'm trying to make Gradle project, not successful. @DaanVanYperen do you know how to import artemis-odb from local folder like lib/artemis-odb? I'd like to choose artemis branch instead of using only current published version. I've also got a problem with importing other Eclipse projects like spine-libgdx.

DaanVanYperen commented 10 years ago
dependencies {
    compile files('libs/something_local.jar')
}
Namek commented 10 years ago

Well, I have figured as much but those are jars.

DaanVanYperen commented 10 years ago

You can set gradle to import from your local maven repository as well. Then compile and install locally and you're set!

DaanVanYperen commented 10 years ago
repositories {
    mavenLocal()
}
Namek commented 10 years ago

Yeah, that's what I've found too. What about artemis? Should I import lib:artemis-odb, lib:artemis-odb:artemis, ...? Tried those two (set settings.gradle file and did compile project(...)) and neither works. I mean calling gradlew.bat build starts java compilation which doesn't find artemis classes like Entity, World etc.

DaanVanYperen commented 10 years ago

So basically. use mvn to compile and install the branch you want into your local maven repo. As long as @junkdog doesn't push out a new snapshot yours should be the one gradle will resolve to. Or you could just change the version number of artemis-odb.

You can also tell gradle to use a specific snapshot if you prefer:

dependencies {
    compile "androidannotations:androidannotations:2.2-20111031.082334-13"
}
DaanVanYperen commented 10 years ago

Yeah, that's what I've found too. What about artemis? Should I import lib:artemis-odb, lib:artemis-odb:artemis, ...? Tried those two (set settings.gradle file and did compile project(...)) and neither works. I mean calling gradlew.bat build starts java compilatio which doesn't find artemis classes like Entity, World etc.

It uses the same qualifiers as maven, so:

dependencies {
    compile "group-id:artefact-id:version"
}

Check out my example gradle+artemis+libgdx project here. https://github.com/DaanVanYperen/libgdx-artemis-quickstart/blob/master/build.gradle

Edit: It has a lot of crap in it maybe, but should be able to glean the stubs you need.

DaanVanYperen commented 10 years ago

if you are asking how to chain-compile a maven project (artemis-odb) with gradle then I don't know, it's probably easier just to use maven repo for that.

Namek commented 10 years ago

I used to have artemis and few other libs as git submodules. When I did compile "net.onedaybeard.artemis:artemis-odb:0.7.2-SNAPSHOT" then the artemis is not found. 0.7.1 is ok. [here starts the rant] The local maven repo is a ridiculous thing to me. What if someone else wanted to import the whole game project? I have to tell him "create some local maven repo and there few things...". And what about projects which are Eclipse-based but not having Maven, Gradle or even Ant builders (like spine-libgdx)? There's already configured .classpath, I don't want to configure it second time in my builder -_-' I'm searching wrong or there will be reasons to hate. And that's all because I wanted newer libgdx (I know I still can use jars but that's officially deprecated).

junkdog commented 10 years ago

When I did compile "net.onedaybeard.artemis:artemis-odb:0.7.2-SNAPSHOT"

Probably because it hasn't been installed (mvn clean install) into the local maven repo.

What if someone else wanted to import the whole game project?

Which is why you don't want IDE-specific configuration - even if most popular IDE:s can import from each other, most of the time.

You could also issue mvn eclipse:eclipse - makes maven generate eclipse project files for the modules. Doesn't always work as intended though. Other IDE:s tend to have much better maven integration, so generating IDE-specific files isn't really necessary for those.

Didn't know that spine rt:s lacked .pom and/or .gradle - Nate is usually pretty good at providing maven files or at least publishing to maven's central repository.

I have to tell him "create some local maven repo and there few things..."

No, maven sets it up automatically - it should work straight out of the box when using release versions - as long as projects are configured as maven or gradle projects (or any other build system which relies on maven's dependency management).

DaanVanYperen commented 10 years ago

I'm fairly certain you /can/ set it up as you want. Gradle is insanely flexible. But right now, compiling it the boring inconvenient way is the first step towards converting your project. Worry about getting your game to run first and after that fix the dependencies!

So in your case, just generate the jars from your third party projects, and circumvent the whole maven thing.

Do you have (a lot of) customization in those third party libraries? If not, by convention I'd try to get used to using maven repositories. In case of someone using your game project, if you want to convenience your users you either use the stock third party libraries, or publish your own artifacts somewhere with a different groupid.

Nb for projects that lacks maven, you can always just package the jars. Typically your game users have no need to edit third party libs directly.

DaanVanYperen commented 10 years ago

Another tip, you can add maven repo's to gradle to grab the latest artemis-odb snapshot.

    repositories {
        mavenLocal()
        mavenCentral()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
    }
DaanVanYperen commented 10 years ago

I don't really have any experience with gradle - looked at it, but felt too much like a more modern ant (not my cup of tea) - but it looks relatively straighforward.

I have to say, it's nowhere near as bad as ant was. But I really can't see a reason to use it when your project build process is as straightforward as small games or artemis.

I can understand using it for a complex build where the rigidity of maven is an issue, but all its idiosyncrasies, black boxed caching and limited IDE support make it a real pain in the butt sometimes.

DaanVanYperen commented 10 years ago

Still, I find the workflow of gradle projects easier to follow at times.

Namek commented 10 years ago

Yeah, Gradle itself looks nice but doesn't work for me right now. There comes problem after problem. I can't even import project generated a minutes ago by gdx-setup into IntelliJ IDEA (wanted to try this IDE, since I use Eclipse for Java) due to lack of some android plugin (changing versions didn't work). Thanks for your help guys. I'll try Gradle when I'll have another half a day.

Do you have (a lot of) customization in those third party libraries? If not, by convention I'd try to get used to using maven repositories.

Actually, there's not that much. First, I have libgdx-based project. my-game-core project should import my game-components-2. The latest imports artemis-odb, mbassador (has pom.xml), spine-libgdx, libgdx and few jars (tween-engine-api, lombok, luaj). I've found that I can setup local maven repo in the project directory, it doesn't have to be in system user directory. But since I don't know maven probably new problems will appear and I don't really want to waste this weekend for them. I'll sure be back with this topic to myself. Just wanted to test new Archetypes because they look awesome!

DaanVanYperen commented 10 years ago

can't even import project generated a minutes ago by gdx-setup into IntelliJ IDEA (wanted to try this IDE, since I use Eclipse for Java) due to lack of some android plugin (changing versions didn't work).

For an easier time, remove 'android, IOS' from settings.gradle, then comment out the 'android/IOS' blocks in the main build.gradle. Also comment out the ios/android related libs all the way at the start in the 'buildscript' part. That should get you compiling for desktop at least.

the libgd android/ios gradle aspects slow IntellIJ compile down to a crawl for me, I'm just leaving them out till it gets fixed.

junkdog commented 10 years ago

@Namek is your project open source? If so I could take a look at it, if you'd want a maven setup (as gradle is too alien to me).

I think it might be prudent to make our own version of gdx-setup, but tie it into artemis-odb-cli instead:

I usually just copy a stubbed project and replace stuff as necessary, but it's a little error prone and not really convenient.

This will probably have to wait until 0.9.0 though.

Namek commented 10 years ago

@junkdog Currently it's not open source. I'm just playing on it with some programming techniques from time to time so it's development is really getting slow. That's why it's not opened (EDIT: but it will be when DaanVanYperen/artemis-odb-contrib#5 will be closer).

At the moment I'd like to just use new Archetypes in current setup. I tried to build .jar consisting of artemis-odb-processor and artemis-odb, did put it into project properties > Java compiler > Annotation Processing and project properties > Java compiler > Annotation Processing > Factory path and... doesn't work.

junkdog commented 10 years ago

Tried fixing it yesterday. Not much luck in the automation department, but I have it working on my end now. I'll push as soon as I finish some validation code.

edit: push in less than 1h.

junkdog commented 10 years ago

Eclipse how-to

Namek commented 10 years ago

There's only one issue while doing mvn package in artemis-processor folder:

[ERROR] Failed to execute goal on project artemis-odb-processor: Could not resolve dependencies for project net.onedaybeard.artemis:artemis-odb-processor:jar:0.7.2-SNAPSHOT: Failed to collect dependencies at net.onedaybeard.artemis:artemis-odb:jar:0.7.2-SNAPSHOT: Failed to read artifact descriptor for net.onedaybeard.artemis:artemis-odb:jar:0.7.2-SNAPSHOT: Could not find artifact net.onedaybeard.artemis:artemis-parent:pom:0.7.2-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots) -> [Help 1]

Probably because it hasn't been installed (mvn clean install) into the local maven repo.

Probably... I need a small help?

junkdog commented 10 years ago

Try mvn clean install from the root - this will compile and install all artemis-odb artifacts into the local m2 repo.

DaanVanYperen commented 10 years ago

Probably... I need a small help?

  • Make sure you have maven installed.
  • Open a dos box (start -> cmd) and navigate to your artemis-odb branch root folder.
  • type mvn clean install
  • no errors means it's in your local repo now!

curses!

Namek commented 10 years ago

Damn, I did it but I thought artemis subfolder would be enough. Root folder worked, thanks! I almost understand that Maven-thing!

Namek commented 10 years ago

currently only support for primitive types and strings

is it forever? Unfornate for me I use Vector2 from libgdx instead of float x,y.

Namek commented 10 years ago

There's an issue with inheritance:

public interface BulletArchetype extends EntityFactory<BulletArchetype> {
    @CRef(Velocity.class) BulletArchetype velocity(float maxSpeed);
}

@CRef({Renderable.class, SpriteComponent.class})
public interface MissileArchetype extends BulletArchetype  {
}

got an error: Interface must implement com.artemis.EntityFactory

DaanVanYperen commented 10 years ago
public interface BulletArchetype<T> extends EntityFactory<T> {
    @CRef(Velocity.class) T velocity(float maxSpeed);
}

@CRef({Renderable.class, SpriteComponent.class})
public interface MissileArchetype extends BulletArchetype<MissileArchetype>  {
}
Namek commented 10 years ago

Still the same error.

junkdog commented 10 years ago

is it forever? Unfornate for me I use Vector2 from libgdx instead of float x,y.

Not sure how to deal with it - the workaround atm is awaiting completion of #198

Probably add an annotation for it in the future, I did it before; it gets a little verbose unfortunately.

got an error: Interface must implement com.artemis.EntityFactory

Should be fixed right away: #203

junkdog commented 10 years ago

Actually, #198 won't be an adequate workaround, at least not the first iteration.

Namek commented 10 years ago

Not sure how to deal with it - the workaround atm is awaiting completion of #198

Ooh, so it just lists component fields? I tried to do the following

@CRef(Velocity.class) T velocity(float maxSpeed, float a, float b, float c, float g, float asdasdsa, float n, float m);

and no errors

while it looks like this:

public class Velocity extends PooledComponent {
    public final Vector2 velocity = new Vector2();
    public final Vector2 acceleration = new Vector2();
    public float maxSpeed;
    public float friction;
    public boolean frictionOn;

    public Velocity setup(float maxSpeed) {
        this.maxSpeed = maxSpeed;
        this.friction = 0;
        this.frictionOn = false;
        return this;
    }

    public Velocity setup(float maxSpeed, float friction) {
        this.maxSpeed = maxSpeed;
        this.friction = friction;
        this.frictionOn = true;
        return this;
    }

    public float getCurrentSpeed() {
        return velocity.len();
    }

    @Override
    protected void reset() {
        velocity.set(acceleration.set(0, 0));
        maxSpeed = friction = 0;
        this.frictionOn = false;
    }
}

So here's the question to be sure - if I make such method:

@CRef(Velocity.class) T velocityParam(float friction); //normally I would name that method friction but would broke example for my question

it will set the value into maxSpeed or friction?

junkdog commented 10 years ago

and no errors

Hmm, I'll look into it right away. Probably an error in the processor.

it will set the value into maxSpeed or friction?

It should set Velocity.friction

junkdog commented 10 years ago

Hmm, I can't get that velocity method to "not work": screenshot

Namek commented 10 years ago

Yeah, a moment ago, when I pulled new code and built processor again I had the same result as you. Then I played with code a little (tried to add/remove generic into/from BulletArchetype) and there are no errors again. Cleaning the project doesn't help, restarting Eclipse neither, turning off annotation processor and turning on again didn't help too.

EDIT: FALSE ALARM!! I dropped type from extends EntityFactory<BulletArchetype>, I had only extends EntityFactory

EDIT 2: but when I do public interface BulletArchetype<T> extends EntityFactory<T> there then errors are gone fore sure.

junkdog commented 10 years ago

EDIT: FALSE ALARM!! I dropped type from extends EntityFactory, I had only extends EntityFactory

Ok, I'll add it to the validation step

Namek commented 10 years ago

So, I'll describe the other issue once again.

Doing

public interface BulletArchetype<T> extends EntityFactory<T> {

instead of

public interface BulletArchetype extends EntityFactory<BulletArchetype> {

makes it possible to write a line

@CRef(Velocity.class) T velocity(float maxSpeed, float a, float b, float c, float g, float asdasdsa, float n, float m);

and see no errors because of non-existing variables.

But still not sure what's the proper way to use inheritance.

junkdog commented 10 years ago

But still not sure what's the proper way to use inheritance.

inheritance isn't available yet: need #203 for that.

I'm guessing you don't see any errors because the annotation processor fails to detect and report the mismatched component fields. Hopefully I can get it sorted out in the next couple of days - about to conclude today's coding session.

DaanVanYperen commented 10 years ago

Great progress overall though @junkdog, :cookie: for you!