New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tags - Modified base system, made basic Skeletal system, added BrokenBone effect. #2

Merged
merged 7 commits into from Jun 3, 2017

Conversation

2 participants
@arpan98
Contributor

arpan98 commented May 27, 2017

Made a map in the player prefab with a AnatomyPartTag which only contains String name for now, but more useful details like a group (upperbody, limbs etc) could be added.

@arpan98 arpan98 changed the title from Tags - Modified prefab and base system to Tags - Modified base system, made basic Skeletal system, added BrokenBone effect. May 30, 2017

@arpan98

This comment has been minimized.

Show comment
Hide comment
@arpan98

arpan98 May 30, 2017

Contributor

I made a basic Skeletal system which has a "bone hp" per part used for tracking the BrokenBoneEffect. Modelled similar to the HealthAuthoritySystem. I am currently using pre-defined percentage values in the system for applying different severities of the BrokenBoneEffect based on hp.

I have only defined the "left leg" and "right leg" as having bones now, because the next step is to introduce a slowing effect based on the severity of the BrokenBoneEffect.

Also made AnatomyEffectAddedEvent and AnatomyEffectRemovedEvent for gathering status info from the systems.

For testing :
It is advisable to increase maxHealth and regenRate of the original HealthSystem so it doesn't interfere with testing using setMaxHealth 10000 and setRegenRate 100. (A workaround for bypassing the previous health system is still to be added for temporary use).

Use damage 5 to simulate damage which should be randomly assigned to one of the parts defined in the prefab. You can use the command showBoneHealths to see the bone HPs of all parts (only legs for now). The HP is also regenerated using similar logic as that in the HealthSystem.

When the HP drops below a certain percentage of the maxHealth, BrokenBone effect will start to be introduced, which can be checked using the command showAnatomyEffects. Something similar to the logic used in this command will be used in the future for the display screen and ragdoll.

The BrokenBone effect will also slow down the player by a percentage depending on the severity of the effect.

Edit: Added speed change on BrokenBoneEffect active.

Contributor

arpan98 commented May 30, 2017

I made a basic Skeletal system which has a "bone hp" per part used for tracking the BrokenBoneEffect. Modelled similar to the HealthAuthoritySystem. I am currently using pre-defined percentage values in the system for applying different severities of the BrokenBoneEffect based on hp.

I have only defined the "left leg" and "right leg" as having bones now, because the next step is to introduce a slowing effect based on the severity of the BrokenBoneEffect.

Also made AnatomyEffectAddedEvent and AnatomyEffectRemovedEvent for gathering status info from the systems.

For testing :
It is advisable to increase maxHealth and regenRate of the original HealthSystem so it doesn't interfere with testing using setMaxHealth 10000 and setRegenRate 100. (A workaround for bypassing the previous health system is still to be added for temporary use).

Use damage 5 to simulate damage which should be randomly assigned to one of the parts defined in the prefab. You can use the command showBoneHealths to see the bone HPs of all parts (only legs for now). The HP is also regenerated using similar logic as that in the HealthSystem.

When the HP drops below a certain percentage of the maxHealth, BrokenBone effect will start to be introduced, which can be checked using the command showAnatomyEffects. Something similar to the logic used in this command will be used in the future for the display screen and ragdoll.

The BrokenBone effect will also slow down the player by a percentage depending on the severity of the effect.

Edit: Added speed change on BrokenBoneEffect active.

Show outdated Hide outdated src/main/java/org/terasology/anatomy/component/AnatomyPartTag.java
public String name = "";
@Replicate
public List<String> effects = Lists.newArrayList();

This comment has been minimized.

@arpan98

arpan98 May 30, 2017

Contributor

The AnatomyX effects are stored here in a list. This is essentially duplicate data of all the BrokenBoneEffect (and other effects to come).
I used Events to send the status info from the AnatomyX systems back to the base system. This duplication had to be done because all the events can't be received simultaneously (they are added when they are registered in their respective systems), so the temporary list had to be stored somewhere. Might as well store it in the component. This will allow the UI to directly look up the list of effects from the component as well.

Modifiable events only support integer values or we could have used them such that each system could add to the list when asked for.

Any alternative suggestions are appreciated.

@arpan98

arpan98 May 30, 2017

Contributor

The AnatomyX effects are stored here in a list. This is essentially duplicate data of all the BrokenBoneEffect (and other effects to come).
I used Events to send the status info from the AnatomyX systems back to the base system. This duplication had to be done because all the events can't be received simultaneously (they are added when they are registered in their respective systems), so the temporary list had to be stored somewhere. Might as well store it in the component. This will allow the UI to directly look up the list of effects from the component as well.

Modifiable events only support integer values or we could have used them such that each system could add to the list when asked for.

Any alternative suggestions are appreciated.

This comment has been minimized.

@Cervator

Cervator May 31, 2017

Member

I'm not catching what you mean here with the events being received simultaneously, could you give an example, maybe on Slack sometime when we get a chance?

And do you think the base Anatomy system actually needs to know? If the AnatomyX systems react to AnatomyPartImpactedEvent and trigger effects there, wouldn't they be attached to the entity rather than tracked by Anatomy at that point? With the AnatomyX systems and their Components tracking when something needs to deactivate.

On Slack InGameHelp came up as an example of the modifyable "pattern" but I'm not sure about the details, probably poke the others in #content that seem to know about it :-)

@Cervator

Cervator May 31, 2017

Member

I'm not catching what you mean here with the events being received simultaneously, could you give an example, maybe on Slack sometime when we get a chance?

And do you think the base Anatomy system actually needs to know? If the AnatomyX systems react to AnatomyPartImpactedEvent and trigger effects there, wouldn't they be attached to the entity rather than tracked by Anatomy at that point? With the AnatomyX systems and their Components tracking when something needs to deactivate.

On Slack InGameHelp came up as an example of the modifyable "pattern" but I'm not sure about the details, probably poke the others in #content that seem to know about it :-)

@Cervator

Very nicely done! I can confirm that this tests out and works great! :-)

It is indeed nice to see this in action, and it is a huuuuge help in better formulating design thoughts. I see both pros and cons with this approach, and have been putting a fair amount of thought into it, but will have to probably get into the details on Slack sometime. Haven't quite had the time tonight to put my thoughts into a coherent response :-)

Will submit one thought in a separate comment here in a moment

Show outdated Hide outdated ...java/org/terasology/anatomy/AnatomySkeleton/SkeletalAuthoritySystem.java
private float bluntDamageMultiplier = 1.5f;
@Override
public void update(float delta) {

This comment has been minimized.

@Cervator

Cervator May 31, 2017

Member

You probably don't need to "tick" for bone regen on every single game loop pass - those systems add up, even if each tick is simple and fast. And it isn't in this case since you're doing a entityManager.getEntitiesWith meaning a full lookup up to 60 times a second :-)

Rather you could either "throttle" the updates (add a check here to skip execution unless x time units have passed since last tick) or use the scheduler (see DelayedActionSystem) instead. With the scheduler you could further decentralize the system where different entities could tick at different times and rates. You'd schedule a regen x seconds ahead at the time where an entity takes non-fatal damage then handle the delayed event when it triggers to apply a regen cycle + schedule the next tick. As a bonus you don't use entityManager.getEntitiesWith at all - each timer triggered event comes with the target entity all ready for you.

After all bones take a while to heal - probably don't need to check for "Are you done yet?!?" 60 times a second :D

Later addition: I do see you have a throttle set indirectly via nextRegenTick but that gets used after you're already processing (it lives in a method being called on every entity with bone each tick), so it doesn't have the performance benefit of checking up front.

@Cervator

Cervator May 31, 2017

Member

You probably don't need to "tick" for bone regen on every single game loop pass - those systems add up, even if each tick is simple and fast. And it isn't in this case since you're doing a entityManager.getEntitiesWith meaning a full lookup up to 60 times a second :-)

Rather you could either "throttle" the updates (add a check here to skip execution unless x time units have passed since last tick) or use the scheduler (see DelayedActionSystem) instead. With the scheduler you could further decentralize the system where different entities could tick at different times and rates. You'd schedule a regen x seconds ahead at the time where an entity takes non-fatal damage then handle the delayed event when it triggers to apply a regen cycle + schedule the next tick. As a bonus you don't use entityManager.getEntitiesWith at all - each timer triggered event comes with the target entity all ready for you.

After all bones take a while to heal - probably don't need to check for "Are you done yet?!?" 60 times a second :D

Later addition: I do see you have a throttle set indirectly via nextRegenTick but that gets used after you're already processing (it lives in a method being called on every entity with bone each tick), so it doesn't have the performance benefit of checking up front.

This comment has been minimized.

@arpan98

arpan98 May 31, 2017

Contributor

Good idea, I think I saw the DelayedActionSystem in use somewhere. I'll take a look.

@arpan98

arpan98 May 31, 2017

Contributor

Good idea, I think I saw the DelayedActionSystem in use somewhere. I'll take a look.

Show outdated Hide outdated ...java/org/terasology/anatomy/AnatomySkeleton/SkeletalAuthoritySystem.java
* This authority system manages the Skeletal system health updates.
*/
@RegisterSystem(value = RegisterMode.AUTHORITY)
public class SkeletalAuthoritySystem extends BaseComponentSystem implements UpdateSubscriberSystem {

This comment has been minimized.

@Cervator

Cervator May 31, 2017

Member

IMHO this class name has some potential for ambiguity - what kind of skeletal system? Meshes? Animation? Zombies? Etc.

Maybe something like BreakableBoneSystem would link it more clearly to Anatomy and the potential outcome. Systems don't have to name themselves after "Authority" or "Client", especially when you only have one of them, it can just be useful when it helps clarify.

Alternatively AnatomicalBoneSystem could tie it to Anatomy even more closely plus suggest the possibility of things other than broken bones, if it one day ends up also tracking arthritis or other bone conditions.

@Cervator

Cervator May 31, 2017

Member

IMHO this class name has some potential for ambiguity - what kind of skeletal system? Meshes? Animation? Zombies? Etc.

Maybe something like BreakableBoneSystem would link it more clearly to Anatomy and the potential outcome. Systems don't have to name themselves after "Authority" or "Client", especially when you only have one of them, it can just be useful when it helps clarify.

Alternatively AnatomicalBoneSystem could tie it to Anatomy even more closely plus suggest the possibility of things other than broken bones, if it one day ends up also tracking arthritis or other bone conditions.

This comment has been minimized.

@arpan98

arpan98 May 31, 2017

Contributor

The names are mostly just placeholders for now. A lot of the variable names might need changing as well. So basically I used the authority system to track HP, so maybe SkeletalHealthSystem?

@arpan98

arpan98 May 31, 2017

Contributor

The names are mostly just placeholders for now. A lot of the variable names might need changing as well. So basically I used the authority system to track HP, so maybe SkeletalHealthSystem?

* A skeletal system which works with Anatomy. Provides a basic bone breaking effect with 3 levels of severity.
*/
@RegisterSystem
public class SkeletalSystem extends BaseComponentSystem {

This comment has been minimized.

@Cervator

Cervator May 31, 2017

Member

Maybe some name ambiguity here vs SkeletalAuthoritySystem ? Since this system is annotated with plain @RegisterSystem it'll act as both authority and client. Should the authority-specific methods be moved to the authority system and this maybe renamed to work as a Client-side @Command centric system?

@Cervator

Cervator May 31, 2017

Member

Maybe some name ambiguity here vs SkeletalAuthoritySystem ? Since this system is annotated with plain @RegisterSystem it'll act as both authority and client. Should the authority-specific methods be moved to the authority system and this maybe renamed to work as a Client-side @Command centric system?

This comment has been minimized.

@arpan98

arpan98 May 31, 2017

Contributor

Should the effect applying be moved to the authority system? It's kinda ambiguous what needs to be in an AuthoritySystem and what doesn't. I only put the health in there because the existing HealthSystem worked that way and it felt important.

@arpan98

arpan98 May 31, 2017

Contributor

Should the effect applying be moved to the authority system? It's kinda ambiguous what needs to be in an AuthoritySystem and what doesn't. I only put the health in there because the existing HealthSystem worked that way and it felt important.

Show outdated Hide outdated src/main/java/org/terasology/anatomy/component/AnatomyPartTag.java
public String name = "";
@Replicate
public List<String> effects = Lists.newArrayList();

This comment has been minimized.

@Cervator

Cervator May 31, 2017

Member

I'm not catching what you mean here with the events being received simultaneously, could you give an example, maybe on Slack sometime when we get a chance?

And do you think the base Anatomy system actually needs to know? If the AnatomyX systems react to AnatomyPartImpactedEvent and trigger effects there, wouldn't they be attached to the entity rather than tracked by Anatomy at that point? With the AnatomyX systems and their Components tracking when something needs to deactivate.

On Slack InGameHelp came up as an example of the modifyable "pattern" but I'm not sure about the details, probably poke the others in #content that seem to know about it :-)

@Cervator

Cervator May 31, 2017

Member

I'm not catching what you mean here with the events being received simultaneously, could you give an example, maybe on Slack sometime when we get a chance?

And do you think the base Anatomy system actually needs to know? If the AnatomyX systems react to AnatomyPartImpactedEvent and trigger effects there, wouldn't they be attached to the entity rather than tracked by Anatomy at that point? With the AnatomyX systems and their Components tracking when something needs to deactivate.

On Slack InGameHelp came up as an example of the modifyable "pattern" but I'm not sure about the details, probably poke the others in #content that seem to know about it :-)

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator May 31, 2017

Member

The one angle I'll mention for now: the tagging system here is quite different from what I had in mind, but to its credit it works, which is more than you can say about my hypothetical design :D

Seeing the player.prefab delta is really helpful, but also points out the danger I was concerned about in some of our Slack talks. While it works beautifully, see how you're using a delta to add Bone details to the player, having to manually synchronize part names between Anatomy and Bone? It does work, but seems fragile to me, introduces a fair bit of duplication (imagine 10 different systems adding their own new component), and leaves the job of actually adding bone details to the author of the bone system (or content authors that determine they want their creatures to explicitly support bone mechanics)

The next challenge after that: how would you also apply bone details to the deer? What about when there are a hundred different creatures? The player is unique as it is essentially the only target you know will be there, so it makes sense to delta. Where would a bone delta for the deer go? There are places where it would work for sure.

By linking an AnatomyX system to its own definition component you introduce a need to actually define it separately, which gets tricky when you scale out. Imagine a hundred creatures and a dozen different AnatomyX systems in each their own module. I'm not saying that's how we should organize it, but doing so would become impractical very fast, and I'm not sure what our options are. Very curious here for feedback from @xrtariq2594 @mjuvekar7, and others

The tagging system I had in mind moves the entirety of the definition challenge to a single AnatomyComponent that looks much like the current one with 2-3 lists added per part. Something like this (pseudo-code, er, json):

    "Anatomy": {
        "parts": {
            "left leg": {
                "characteristics": [
                    "bone",
                    "flesh",
                    "ligament"
                ],
                "abilities": [
                    "mobility:5",
                    "agility:5"
                ]
            },
            "right leg": { ... }
        }
    },

The goal with that would be to semantically describe the body to whichever level of detail the author desires, without ever explicitly invoking any functionality directly (that's what I meant with everything being plain string "tags"). It could even be extended to later show what's connected to what via nesting, although that gets really complicated and should probably be ignored for now. Another utility element would be adding templates or archetypes (much like damage type prefabs) that describe typical limbs like "HumanoidArm" so you don't have to repeat yourself, you can just use a short form of "left arm": "core:humanoidArm", "right arm": :core:humanoidArm"

Like you have it now in AnatomySystem.onDamage the base anatomy would pick a part at random and include all the data for that part in the event. Rather than SkeletalAuthoritySystem.onBoneDamage filtering by BoneComponent it would simply check if the part described by the event has the characteristic "bone" and if so calculate the impact + see if any events should trigger. If a bone breaks the BrokenBoneComponent gets attached, or if already present has the new part added to some map between affected parts and the severity.

That's where your approach has an advantage - you are mapping the parts exactly and including extra details like health, magnitude of effects, and so on. My silly little example is way more basic and just lists some vague things like "mobility:5" meant to indicate that the part plays a role in whatever "mobility" might mean, and that its weight is "5" - likely every part contributing to "mobility" would have their weights added together and that's how you'd figure out how problematic damage to a leg with "mobility:5" would be.

Exactly what "bone" or "mobility" would ultimately mean is left up to AnatomyX authors. So you could have the bone breaking system focusing on combat or falling damage leading to broken bones and reduced movement speed. You could have a completely different system that also cares about bone, but to model the long-term effects of arthritis on how readily you can perform fine hand gestures (crafting speed) based on agility, dexterity, or some other "ability"

Okay this got a little long anyway - consider it a brain dump, not directions! :) More later on Slack sometime I figure. Your approach as written does work wonderfully so you've got more than I do!

One final note: For effects SkeletalEffectsSystem effectively duplicates SpeedAlterationSystem from AlterationEffects, with a little custom logic from basing it on bone health. Instead of that you could potentially react to the handling of a broken bone (while analyzing AnatomyPartImpactedEvent) and trigger an alteration effect of a given magnitude. That reduces duplication and potentially exposes the option of having a spell negating move speed penalties also work on a broken bone (but does require adding AlterationEffects as a dependency whenever logic wants to apply an effect)

Member

Cervator commented May 31, 2017

The one angle I'll mention for now: the tagging system here is quite different from what I had in mind, but to its credit it works, which is more than you can say about my hypothetical design :D

Seeing the player.prefab delta is really helpful, but also points out the danger I was concerned about in some of our Slack talks. While it works beautifully, see how you're using a delta to add Bone details to the player, having to manually synchronize part names between Anatomy and Bone? It does work, but seems fragile to me, introduces a fair bit of duplication (imagine 10 different systems adding their own new component), and leaves the job of actually adding bone details to the author of the bone system (or content authors that determine they want their creatures to explicitly support bone mechanics)

The next challenge after that: how would you also apply bone details to the deer? What about when there are a hundred different creatures? The player is unique as it is essentially the only target you know will be there, so it makes sense to delta. Where would a bone delta for the deer go? There are places where it would work for sure.

By linking an AnatomyX system to its own definition component you introduce a need to actually define it separately, which gets tricky when you scale out. Imagine a hundred creatures and a dozen different AnatomyX systems in each their own module. I'm not saying that's how we should organize it, but doing so would become impractical very fast, and I'm not sure what our options are. Very curious here for feedback from @xrtariq2594 @mjuvekar7, and others

The tagging system I had in mind moves the entirety of the definition challenge to a single AnatomyComponent that looks much like the current one with 2-3 lists added per part. Something like this (pseudo-code, er, json):

    "Anatomy": {
        "parts": {
            "left leg": {
                "characteristics": [
                    "bone",
                    "flesh",
                    "ligament"
                ],
                "abilities": [
                    "mobility:5",
                    "agility:5"
                ]
            },
            "right leg": { ... }
        }
    },

The goal with that would be to semantically describe the body to whichever level of detail the author desires, without ever explicitly invoking any functionality directly (that's what I meant with everything being plain string "tags"). It could even be extended to later show what's connected to what via nesting, although that gets really complicated and should probably be ignored for now. Another utility element would be adding templates or archetypes (much like damage type prefabs) that describe typical limbs like "HumanoidArm" so you don't have to repeat yourself, you can just use a short form of "left arm": "core:humanoidArm", "right arm": :core:humanoidArm"

Like you have it now in AnatomySystem.onDamage the base anatomy would pick a part at random and include all the data for that part in the event. Rather than SkeletalAuthoritySystem.onBoneDamage filtering by BoneComponent it would simply check if the part described by the event has the characteristic "bone" and if so calculate the impact + see if any events should trigger. If a bone breaks the BrokenBoneComponent gets attached, or if already present has the new part added to some map between affected parts and the severity.

That's where your approach has an advantage - you are mapping the parts exactly and including extra details like health, magnitude of effects, and so on. My silly little example is way more basic and just lists some vague things like "mobility:5" meant to indicate that the part plays a role in whatever "mobility" might mean, and that its weight is "5" - likely every part contributing to "mobility" would have their weights added together and that's how you'd figure out how problematic damage to a leg with "mobility:5" would be.

Exactly what "bone" or "mobility" would ultimately mean is left up to AnatomyX authors. So you could have the bone breaking system focusing on combat or falling damage leading to broken bones and reduced movement speed. You could have a completely different system that also cares about bone, but to model the long-term effects of arthritis on how readily you can perform fine hand gestures (crafting speed) based on agility, dexterity, or some other "ability"

Okay this got a little long anyway - consider it a brain dump, not directions! :) More later on Slack sometime I figure. Your approach as written does work wonderfully so you've got more than I do!

One final note: For effects SkeletalEffectsSystem effectively duplicates SpeedAlterationSystem from AlterationEffects, with a little custom logic from basing it on bone health. Instead of that you could potentially react to the handling of a broken bone (while analyzing AnatomyPartImpactedEvent) and trigger an alteration effect of a given magnitude. That reduces duplication and potentially exposes the option of having a spell negating move speed penalties also work on a broken bone (but does require adding AlterationEffects as a dependency whenever logic wants to apply an effect)

@arpan98

This comment has been minimized.

Show comment
Hide comment
@arpan98

arpan98 Jun 1, 2017

Contributor

I set up the AnatomyStatusGatheringEvent as a modifiable event with a Map<String, List<String>>. Also used the DelayManager to schedule regen HP updates instead of checking on every tick.

Contributor

arpan98 commented Jun 1, 2017

I set up the AnatomyStatusGatheringEvent as a modifiable event with a Map<String, List<String>>. Also used the DelayManager to schedule regen HP updates instead of checking on every tick.

@Override
public void update(float delta) {
public void initialise() {

This comment has been minimized.

@Cervator

Cervator Jun 3, 2017

Member

Good optimization, although I wonder if this lifecycle stage might be too early in some cases. Initializing IIRC is more for the system to initialize itself in some fashion, with the overall entity base not necessarily loaded yet (but maybe enough did load for it to appear to work)

An additional optimization would be to only consider entities with injured bones in need of regen, which you could then tie to the event (during regular gameplay) that actually injured the bone in the first place, rather than need to do anything at all on init :-)

@Cervator

Cervator Jun 3, 2017

Member

Good optimization, although I wonder if this lifecycle stage might be too early in some cases. Initializing IIRC is more for the system to initialize itself in some fashion, with the overall entity base not necessarily loaded yet (but maybe enough did load for it to appear to work)

An additional optimization would be to only consider entities with injured bones in need of regen, which you could then tie to the event (during regular gameplay) that actually injured the bone in the first place, rather than need to do anything at all on init :-)

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Jun 3, 2017

Member

Made one more small comment, but good changes in the latest commit :-)

Much more that can be done, but lets do that incrementally in new PRs - merging this one for now, nice work!

Member

Cervator commented Jun 3, 2017

Made one more small comment, but good changes in the latest commit :-)

Much more that can be done, but lets do that incrementally in new PRs - merging this one for now, nice work!

@Cervator Cervator merged commit d233c2f into Terasology:tags Jun 3, 2017

@arpan98 arpan98 moved this from In Progress to Completed in GSOC 2017 - Anatomy System and Genome integrations Jun 13, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment