Design question trying to move away from extending spatials

Hello,



As per:

http://hub.jmonkeyengine.org/groups/general/forum/topic/jme3-i-think-i-get-it-now/



I’m trying to move a lot of my extended spatial classes into control classes. However, I’ve hit a bit of a logic snag. I’ll explain how I’m doing it right now.



Interactive objects extend Geometry and implement a class that I designed that has 3 methods: highlight, dehighlight, and performInteraction.



So, for user interaction, I have a simple picking routine running every update to actively keep up with what the mouse is hovering. I have a special node where any and all the objects the user can interact with reside. When the mouse hovers an object in contained in this node, it calls highlight, and the geometry is highlighted whatever color it is supposed to be (determined by the object, in the case of a locked door it would be red, and an unlocked door would be green etc etc). And if the user clicks on the highlighted geometry, it calls performInteraction, which usually does something along the lines of adding a control to do a one time rotation or translation that removes itself upon completion of whatever movement was required. It can do anything you specify as well, but this is the intent.



Now, having said all of this, how would I go about moving away from extending the geometry? It seems kind of silly to have a control that is simply waiting for performInteraction/highlight calls with an empty update() method. And in the end, it would be a control spawning more controls, which I mean is fine, it just seems weird in this particular usage. Or is this the one instance where it’s OK to go with this? I mean, I’m not even really doing anything super custom to the geometry I guess…



Either way, I’ve attached a sample of how a ladder would work for instance. Though it doesn’t spawn any controls, the concept is simple enough.



Any advice would be appreciated.

~FlaH



[java]

public class Ladder extends Geometry implements InteractiveControl {



ColorRGBA defaultColor;

ColorRGBA highlightColor = ColorRGBA.Yellow;



Player player;

Vector3f facingTowardsLadder;



public Ladder(String name, Mesh mesh, Player player, Vector3f facingTowardsLadder) {

super(name, mesh);



this.player = player;

this.facingTowardsLadder = facingTowardsLadder;

}



@Override

public void deHighlight() {

setColor(defaultColor);

}



@Override

public void performInteraction() {

player.setOnLadder(this.getWorldBound(), facingTowardsLadder);

}



@Override

public void performTriggeredActivation() {

}



@Override

public void setHighlighted() {

storeDefaultColor();

setColor(highlightColor);

}



private void storeDefaultColor() {

if(this.getMaterial().getMaterialDef().getName().contains(“Color”)) {

defaultColor = (ColorRGBA) this.getMaterial().getParam(“Color”).getValue();

}

else {

defaultColor = (ColorRGBA) this.getMaterial().getParam(“Diffuse”).getValue();

}

}



private void setColor(ColorRGBA newColor) {

if(this.getMaterial().getMaterialDef().getName().contains(“Color”)) {

this.getMaterial().getParam(“Color”).setValue(newColor);

}

else {

this.getMaterial().getParam(“Diffuse”).setValue(newColor);

}

}

}

[/java]

Did you consider to use an AppState for the picking?

AppStates are a bit similar to Controls, but with the difference that they are not applied to a Spatial, but are global to the application.

If i understand correctly the picking behavior is global in your application, so it would make sense that an AppState handle the picking.



However, your highlight/interact control solution sounds good, but don’t spawn controls on the fly, you can extend it just to fit the particular need of a pickable object, and just add it on initialization of the scene. The picker AppState would enable them when they are picked.



For example your door : the door control would extend the InteractionControl and the “interact” method would just open the door when it’s close and close it when it’s open. But the highlighting part would be handled by the parent class InteractionControl



Of course there are plenty valid ways to do this, this is just “a” way :stuck_out_tongue:

Hello again!



Actually, I hadn’t thought of trying an AppState for the picking. Is there an advantage to doing it this way? Right now, it just follows the PlayerControl around since there’s a range to how far the player can interact with something (3rd person game).



Yeah, my “plan” (lol) is to have the levels parsed on load to build all the structures appropriately before handing control over to the player. So if an object is supposed to be a door, this is specified by the mesh name in the ogre file and my parser will do the rest of the magic from another xml file probably, but that’s pretty far away at the moment so I’m not too worried about it just yet. (Loose plan is to have the name like door1000 and the parse would look at the other xml file and look for a definition for door1000 and fill in the blanks to build the door (locked, rotation angle, etc etc) Wanted to look into that whole UserData thing too but I don’t understand if that comes from blender or not)



So… What you’re saying for the door is to stop extending the Geometry, make the InteractionControl a real control (it’s not… yet, it was actually InteractiveGeometry before, but I must’ve copy-pasted my halfass thinking that I was messing with), and in the update for the control maybe check for a change in the door’s state? Of course keeping track of this yatta yatta, and the interact simply handles the switching between the two states? Ooooh but this would all happen in another control that extends InteractiveControl, almost forgot that.



I’ll go play with this a bit, but I’ll be refreshing over here :stuck_out_tongue:



Thanks!

~FlaH

Hello Again!



Just figured I’d pop back into this thread and show what I did in case it helps anyone.



Still would like your input on my additional question on using AppStates for the mouse picking @nehon, if you have time. ;p



Also, on a whim, I ran around and saw the TestUserData in the jME tests and that is some really cool stuff. I figured out how to add properties to objects in blender and export it out via the scene export and then read in some values for a simple object. That is definitely going to help a ton when I start moving away from hardcoding and making the game’s level work for me instead.



Below is what my ladder class turned into. It’s not perfect, but it’s better to work with. I need to work out the material usage and dependency on the constructor a bit, but it’s a step forward from what I was doing before this.



Thanks @nehon!

~FlaH



Ladder.java

[java]

public class Ladder implements InteractiveControl {



private boolean enabled = true;

private Spatial spatial;



ColorRGBA defaultColor;

ColorRGBA highlightColor = ColorRGBA.Yellow;



Material material;



Vector3f facingTowardsLadder;



public Ladder(Material material, Vector3f facingTowardsLadder) {

this.material = material;

this.facingTowardsLadder = facingTowardsLadder;

}



@Override

public void deHighlight() {

setColor(defaultColor);

}



@Override

public void performInteraction() {

Main.getPlayerControl().getMoveControl().setOnLadder(spatial.getWorldBound(), facingTowardsLadder);

}



@Override

public void performTriggeredActivation() {

}



@Override

public void setHighlighted() {

storeDefaultColor();

setColor(highlightColor);

}



private void storeDefaultColor() {

if(material.getMaterialDef().getName().contains(“Color”)) {

defaultColor = (ColorRGBA) material.getParam(“Color”).getValue();

}

else {

defaultColor = (ColorRGBA) material.getParam(“Diffuse”).getValue();

}

}



private void setColor(ColorRGBA newColor) {

if(material.getMaterialDef().getName().contains(“Color”)) {

material.getParam(“Color”).setValue(newColor);

}

else {

material.getParam(“Diffuse”).setValue(newColor);

}

}



@Override

public void update(float tpf) {

if (!enabled) {

return;

}

}



@Override

public boolean isEnabled() {

return enabled;

}



@Override

public void setEnabled(boolean enabled) {

this.enabled = enabled;

}



@Override

public void setSpatial(Spatial spatial) {

this.spatial = spatial;

}



@Override

public void render(RenderManager rm, ViewPort vp) {

}



@Override

public Control cloneForSpatial(Spatial spatial) {

return null;

}



@Override

public void read(JmeImporter im) throws IOException {

// TODO Auto-generated method stub



}



@Override

public void write(JmeExporter ex) throws IOException {

// TODO Auto-generated method stub



}

}

[/java]



InteractiveControl.java

[java]

public interface InteractiveControl extends Control {



/**

  • This is called when you want this interactive Geometry to perform whatever movement,
  • rotation, or action that has been scripted for it to do.

    */

    public void performInteraction();



    /**
  • This is called when you want this interactive Geometry to perform an action that is
  • meant to be triggered by a button, switch, or lever.

    */

    public void performTriggeredActivation();



    /**
  • Highlight the geometry’s material with a color.

    */

    public void setHighlighted();



    /**
  • Remove any highlight that has been applied to this geometry’s material.

    */

    public void deHighlight();

    }

    [/java]

Ok this is good, but, I would have used an abstract class instead of the interface for InteractiveControl (that would extend AbstractControl).

here with your interface, you have to re-implement the “setEnabled” part and the setHighlight, setDefaultColor… for each control you will implement.

That’s a lot of duplicate code.



with an abstract class you can implement all common behavior the controls will have in the parent classe, and focus on the specific behavior of the child control, which would be performInteraction() and performTriggeredActivation().