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
-
You need to have mechanics for breeding or mating, it can utilise the Events or Entity System.
-
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.
- Set the breeding algorithm to use with your organisms and create the
genomeID
to be assigned to the oraganisms' Gene Component. Also calledaddPropertyMap
, passinganimal1
,animal2
(from the mating event) andgenomeId
.
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);
}
- 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());
}
- 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.
- Create the offspring (using
EntityManager
) and send new eventonBreed
, passinganimal1
,animal2
andoffspring
, in order to usebreedingAlgorithm
in determining the genes of the offspring. The complete code ofonMatingStart
:
@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.