Implementation - Terasology/Genome GitHub Wiki

Implementing Genome Into Your Own Module

Read "How does it work?" first

The following steps will use the example from WildAnimalsGenome Module which uses Genome to make a deer's speed dependent on its genes.

Setting up system

  1. You need to have mechanics for breeding or mating, it can utilise the Events or Entity System.

  2. Create the system that handles the integration of Genome.

@RegisterSystem(RegisterMode.AUTHORITY)
public class AnimalGeneticsSystem extends BaseComponentSystem {
    @ReceiveEvent
    public void onMatingStart(MatingInitiatedEvent event, EntityRef entityRef, MatingComponent matingComponent) {
    }

    private void addPropertyMap(EntityRef animal1, EntityRef animal2, String genomeID) {
    }
}

onMatingStart will be called upon your mating event. This will be used to handle the Genome Component and production of offspring of your 2 organisms, in this case animals.

addPropertyMap will be used to handle Genome Definition which contains the organism's breeding algorithm and its genome map. It will handle Genome Definition for both parents and register them to the genomeRegistry.

  1. Set the breeding algorithm to use with your organisms and create the genomeIDto be assigned to the oraganisms' Gene Component. Also called addPropertyMap, passing animal1, animal2 (from the mating event) and genomeId .
private static final String genomeRegistryPrefix = "WildAnimals:";

private BreedingAlgorithm breedingAlgorithm;

@Override
    public void preBegin() {
        breedingAlgorithm = new FavourableWeightedBreedingAlgorithm(0, 0.6f);
    }

@ReceiveEvent
    public void onMatingStart(MatingInitiatedEvent event, EntityRef entityRef, MatingComponent matingComponent) {
        // Build a unique genomeID for insertion into the registry.
        String genomeID = genomeRegistryPrefix + event.animal1.getId() + ":" + event.animal2.getId();
        addPropertyMap(event.animal1, event.animal2, genomeID);
    }
  1. Add Genome Component to both organisms (Add to onMatingStart), if they don't have one.
        if (!event.animal1.hasComponent(GenomeComponent.class)) {
            event.animal1.addComponent(new GenomeComponent());
        }

        if (!event.animal2.hasComponent(GenomeComponent.class)) {
            event.animal2.addComponent(new GenomeComponent());
        }
  1. Set the genes and genomeId of both organisms' Gene Component (Add to onMatingStart).
        Float speedMultiplier1 = event.animal1.getComponent(CharacterMovementComponent.class).speedMultiplier;
        Float speedMultiplier2 = event.animal2.getComponent(CharacterMovementComponent.class).speedMultiplier;

        GenomeComponent genomeComponent1 = event.animal1.getComponent(GenomeComponent.class);
        GenomeComponent genomeComponent2 = event.animal2.getComponent(GenomeComponent.class);

        genomeComponent1.genes = speedMultiplier1 >= speedMultiplier2 ? "B" : "A";
        genomeComponent2.genes = speedMultiplier1 >= speedMultiplier2 ? "A" : "B";

        genomeComponent1.genomeId = genomeID;
        genomeComponent2.genomeId = genomeID;

        event.animal1.saveComponent(genomeComponent1);
        event.animal2.saveComponent(genomeComponent2);

In this example, the genes of both organisms are set according to their speedMultiplier component. Don't forget to save the Genome Component after setting it.

  1. Create the offspring (using EntityManager) and send new event onBreed, passing animal1, animal2 and offspring, in order to use breedingAlgorithm in determining the genes of the offspring. The complete code of onMatingStart:
    @ReceiveEvent
    public void onMatingStart(MatingInitiatedEvent event, EntityRef entityRef, MatingComponent matingComponent) {
        // Build a unique genomeID for insertion into the registry.
        String genomeID = genomeRegistryPrefix + event.animal1.getId() + ":" + event.animal2.getId();
        addPropertyMap(event.animal1, event.animal2, genomeID);

        if (!event.animal1.hasComponent(GenomeComponent.class)) {
            event.animal1.addComponent(new GenomeComponent());
        }

        if (!event.animal2.hasComponent(GenomeComponent.class)) {
            event.animal2.addComponent(new GenomeComponent());
        }

        Float speedMultiplier1 = event.animal1.getComponent(CharacterMovementComponent.class).speedMultiplier;
        Float speedMultiplier2 = event.animal2.getComponent(CharacterMovementComponent.class).speedMultiplier;

        GenomeComponent genomeComponent1 = event.animal1.getComponent(GenomeComponent.class);
        GenomeComponent genomeComponent2 = event.animal2.getComponent(GenomeComponent.class);

        genomeComponent1.genes = speedMultiplier1 >= speedMultiplier2 ? "B" : "A";
        genomeComponent2.genes = speedMultiplier1 >= speedMultiplier2 ? "A" : "B";

        genomeComponent1.genomeId = genomeID;
        genomeComponent2.genomeId = genomeID;

        event.animal1.saveComponent(genomeComponent1);
        event.animal2.saveComponent(genomeComponent2);

        EntityRef offspring;
        if (event.animal1.getParentPrefab().getName().equals("WildAnimals:deer")) {
            offspring = entityManager.create("WildAnimals:babyDeer");
        } else {
            offspring = entityManager.create(event.animal1.getParentPrefab());
        }
        entityRef.send(new OnBreed(event.animal1, event.animal2, offspring));
    }

Don't forget to declare EntityManager:

    @In
    private EntityManager entityManager;

Genome Definition and Registry

It wouldn't work now because we haven't set up the Genome Definition, as this contains the genome map and the breeding algorithm. The mechanic is that it's going to use breeding algorithm in the organism's Genome Definition in order to breed. Now go to addPropertyMap:

    @In
    private GenomeRegistry genomeRegistry;

    @In
    private WorldProvider worldProvider;

    private void addPropertyMap(EntityRef animal1, EntityRef animal2, String genomeID) {
        SeedBasedGenomeMap genomeMap = new SeedBasedGenomeMap(worldProvider.getSeed().hashCode());
        genomeMap.addSeedBasedProperty("speedMultiplier", 0, 0, 1, Float.class,
                new Function<String, Float>() {
                    @Nullable
                    @Override
                    public Float apply(@Nullable String input) {
                        Float speedMultiplier1 = animal1.getComponent(CharacterMovementComponent.class).speedMultiplier;
                        Float speedMultiplier2 = animal2.getComponent(CharacterMovementComponent.class).speedMultiplier;
                        if (input.charAt(0) == 'A') {
                            return speedMultiplier1 >= speedMultiplier2 ? speedMultiplier2 : speedMultiplier1;
                        } else {
                            return speedMultiplier1 >= speedMultiplier2 ? speedMultiplier1 : speedMultiplier2;
                        }
                    }
                });
        GenomeDefinition genomeDefinition = new GenomeDefinition(breedingAlgorithm, genomeMap);
        genomeRegistry.registerType(genomeID, genomeDefinition);

    }

The crucial part is setting the Genome Definition, using selected breeding algorithm and the organism's genome map. Genome Definition will then need to be registered to the genomeRegistry. The genome map in this example is set using a pseudo-random generator which uses the game's seed.