|
@@ -7,513 +7,9 @@
|
|
|
ifdef::env-github,env-browser[:outfilesuffix: .adoc]
|
|
|
|
|
|
|
|
|
+== Normen Hansen Videos
|
|
|
|
|
|
-== How to control any scene node source code
|
|
|
-
|
|
|
-.RotatingControl.java
|
|
|
-[source,java]
|
|
|
-----
|
|
|
-
|
|
|
-package mygame;
|
|
|
-import com.jme3.renderer.RenderManager;
|
|
|
-import com.jme3.renderer.ViewPort;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import com.jme3.scene.control.AbstractControl;
|
|
|
-import com.jme3.scene.control.Control;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author normenhansen
|
|
|
- */
|
|
|
-public class RotatingControl extends AbstractControl {
|
|
|
-
|
|
|
- private float speed = 1;
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlUpdate(float tpf) {
|
|
|
- spatial.rotate(0, 0, tpf * speed);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
|
- //Only needed for rendering-related operations,
|
|
|
- //not called when spatial is culled.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
- RotatingControl control = new RotatingControl();
|
|
|
- control.setSpeed(speed);
|
|
|
- control.setSpatial(spatial);
|
|
|
- return control;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return the speed
|
|
|
- */
|
|
|
- public float getSpeed() {
|
|
|
- return speed;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @param speed the speed to set
|
|
|
- */
|
|
|
- public void setSpeed(float speed) {
|
|
|
- this.speed = speed;
|
|
|
- }
|
|
|
-}
|
|
|
-----
|
|
|
-
|
|
|
-
|
|
|
-== How to control a character in a scene source code
|
|
|
-
|
|
|
-The source code and assets for the Normen Hansen video link:http://www.youtube.com/watch?v=MNDiZ9YHIpM[Video: How to control any scene node] and link:http://www.youtube.com/watch?v=-OzRZscLlHY[Video: How to remote control a character in a scene] can be found in the zip archive link:https://github.com/jMonkeyEngine/wiki/blob/master/src/docs/resources/Scenes/SDK-UsecaseDemo_1.zip[SDK-UsecaseDemo_1.zip].
|
|
|
+The source code and assets for the Normen Hansen video link:http://www.youtube.com/watch?v=MNDiZ9YHIpM[How to control any scene node] and link:http://www.youtube.com/watch?v=-OzRZscLlHY[How to remote control a character in a scene] can be found in the zip archive link:https://github.com/jMonkeyEngine/wiki/raw/master/src/docs/resources/Scenes/SDK-UsecaseDemo_1.zip[SDK-UsecaseDemo_1.zip].
|
|
|
|
|
|
You can read about this code on the jMonkeyEngine forum thread, link:https://hub.jmonkeyengine.org/t/new-sdk-video-tutorial-latest-vol2/25185[New Sdk Video Tutorial (Latest:Vol2)].
|
|
|
|
|
|
-.ConfigAppState.java
|
|
|
-
|
|
|
-[source,java]
|
|
|
-----
|
|
|
-
|
|
|
-package mygame;
|
|
|
-
|
|
|
-import com.jme3.app.Application;
|
|
|
-import com.jme3.app.SimpleApplication;
|
|
|
-import com.jme3.app.state.BaseAppState;
|
|
|
-import com.jme3.scene.Node;
|
|
|
-import com.jme3.scene.SceneGraphVisitor;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import controls.QuixoteControl;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author normenhansen
|
|
|
- */
|
|
|
-public class ConfigAppState extends BaseAppState {
|
|
|
-
|
|
|
- private SimpleApplication app;
|
|
|
- private QuixoteState state = QuixoteState.RELAXED;
|
|
|
- private Spatial quixote;
|
|
|
- private boolean quixoteRunning = false;
|
|
|
-
|
|
|
- public enum QuixoteState {
|
|
|
- AGITATED, RELAXED, SLEEPING
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void initialize(Application app) {
|
|
|
- this.app = (SimpleApplication) app;
|
|
|
- setQuixoteRunning(true);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void cleanup(Application app) {
|
|
|
- //TODO: clean up what you initialized in the initialize method,
|
|
|
- //e.g. remove all spatials from rootNode
|
|
|
- }
|
|
|
-
|
|
|
- //onEnable()/onDisable() can be used for managing things that should
|
|
|
- //only exist while the state is enabled. Prime examples would be scene
|
|
|
- //graph attachment or input listener attachment.
|
|
|
- @Override
|
|
|
- protected void onEnable() {
|
|
|
- //Called when the state is fully enabled, ie: is attached and
|
|
|
- //isEnabled() is true or when the setEnabled() status changes after the
|
|
|
- //state is attached.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onDisable() {
|
|
|
- //Called when the state was previously enabled but is now disabled
|
|
|
- //either because setEnabled(false) was called or the state is being
|
|
|
- //cleaned up.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void update(float tpf) {
|
|
|
- //TODO: implement behavior during runtime
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return the quixoteRunning
|
|
|
- */
|
|
|
- public boolean isQuixoteRunning() {
|
|
|
- return quixoteRunning;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @param quixoteRunning the quixoteRunning to set
|
|
|
- */
|
|
|
- public void setQuixoteRunning(boolean quixoteRunning) {
|
|
|
- this.quixoteRunning = quixoteRunning;
|
|
|
- if (quixoteRunning) {
|
|
|
- quixote = getApplication().getAssetManager().loadModel(
|
|
|
- "Models/Quixote.j3o");
|
|
|
- applyQuixoteState(quixote, state);
|
|
|
- Node scene = (Node) app.getRootNode().getChild("Scene");
|
|
|
- if (scene != null) {
|
|
|
- scene.attachChild(quixote);
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (quixote != null) {
|
|
|
- quixote.removeFromParent();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return the state
|
|
|
- */
|
|
|
- public QuixoteState getState() {
|
|
|
- return state;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @param state the state to set
|
|
|
- */
|
|
|
- public void setState(QuixoteState state) {
|
|
|
- this.state = state;
|
|
|
- QuixoteState quxState = state;
|
|
|
- app.getRootNode().depthFirstTraversal(new SceneGraphVisitor() {
|
|
|
- @Override
|
|
|
- public void visit(Spatial spatial) {
|
|
|
- applyQuixoteState(spatial, quxState);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- private void applyQuixoteState(Spatial spatial, QuixoteState state) {
|
|
|
- QuixoteControl control = spatial.getControl(QuixoteControl.class);
|
|
|
- if (control != null) {
|
|
|
- switch (state) {
|
|
|
- case AGITATED:
|
|
|
- control.setSpeed(5.0f);
|
|
|
- break;
|
|
|
- case RELAXED:
|
|
|
- control.setSpeed(1.0f);
|
|
|
- break;
|
|
|
- case SLEEPING:
|
|
|
- control.setSpeed(0.0f);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-----
|
|
|
-
|
|
|
-.QuixoteControl.java
|
|
|
-
|
|
|
-[source,java]
|
|
|
-----
|
|
|
-
|
|
|
-package mygame;
|
|
|
-
|
|
|
-import com.jme3.math.Quaternion;
|
|
|
-import com.jme3.math.Vector3f;
|
|
|
-import com.jme3.renderer.RenderManager;
|
|
|
-import com.jme3.renderer.ViewPort;
|
|
|
-import com.jme3.scene.Node;
|
|
|
-import com.jme3.scene.SceneGraphVisitor;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import com.jme3.scene.control.AbstractControl;
|
|
|
-import com.jme3.scene.control.Control;
|
|
|
-import java.util.LinkedList;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Random;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author normenhansen
|
|
|
- */
|
|
|
-public class QuixoteControl extends AbstractControl {
|
|
|
-
|
|
|
- private Spatial myMill;
|
|
|
- private float speed = 1.0f;
|
|
|
- private static Random rnd = new Random(System.currentTimeMillis());
|
|
|
- private final Quaternion lookRotation = new Quaternion();
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlUpdate(float tpf) {
|
|
|
- if (myMill == null) {
|
|
|
- millers();
|
|
|
- }
|
|
|
- runToTheMills(tpf);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
|
- //Only needed for rendering-related operations,
|
|
|
- //not called when spatial is culled.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
- QuixoteControl control = new QuixoteControl();
|
|
|
- control.setSpeed(speed);
|
|
|
- control.setSpatial(spatial);
|
|
|
- return control;
|
|
|
- }
|
|
|
-
|
|
|
- public void millers() {
|
|
|
- Node node = spatial.getParent();
|
|
|
- if (node != null) {
|
|
|
- final List<Spatial> mills = new LinkedList<>();
|
|
|
- node.depthFirstTraversal(new SceneGraphVisitor() {
|
|
|
- @Override
|
|
|
- public void visit(Spatial spatial) {
|
|
|
- if (spatial.getName().equals("Models/RotatingMill.j3o")) {
|
|
|
- mills.add(spatial);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- if (mills.isEmpty()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- int no = (int) ((mills.size() - 1) * rnd.nextFloat());
|
|
|
- myMill = mills.get(no);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void runToTheMills(float tpf) {
|
|
|
- if (myMill != null) {
|
|
|
- Vector3f aim = myMill.getWorldTranslation();
|
|
|
- Vector3f dist = aim.subtract(spatial.getWorldTranslation());
|
|
|
- if (dist.length() < 1) {
|
|
|
- myMill = null;
|
|
|
- } else {
|
|
|
- dist.normalizeLocal();
|
|
|
- lookRotation.lookAt(dist, Vector3f.UNIT_Y);
|
|
|
- spatial.setLocalRotation(lookRotation);
|
|
|
- spatial.move(dist.multLocal(speed * tpf));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @return the speed
|
|
|
- */
|
|
|
- public float getSpeed() {
|
|
|
- return speed;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @param speed the speed to set
|
|
|
- */
|
|
|
- public void setSpeed(float speed) {
|
|
|
- this.speed = speed;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-----
|
|
|
-
|
|
|
-.TerrainTrackControl.java
|
|
|
-
|
|
|
-[source,java]
|
|
|
-----
|
|
|
-
|
|
|
-package mygame;
|
|
|
-
|
|
|
-import com.jme3.collision.CollisionResult;
|
|
|
-import com.jme3.collision.CollisionResults;
|
|
|
-import com.jme3.export.InputCapsule;
|
|
|
-import com.jme3.export.JmeExporter;
|
|
|
-import com.jme3.export.JmeImporter;
|
|
|
-import com.jme3.export.OutputCapsule;
|
|
|
-import com.jme3.math.Ray;
|
|
|
-import com.jme3.math.Vector3f;
|
|
|
-import com.jme3.renderer.RenderManager;
|
|
|
-import com.jme3.renderer.ViewPort;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import com.jme3.scene.control.AbstractControl;
|
|
|
-import com.jme3.scene.control.Control;
|
|
|
-import java.io.IOException;
|
|
|
-import java.util.Iterator;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author normenhansen
|
|
|
- */
|
|
|
-public class TerrainTrackControl extends AbstractControl {
|
|
|
- //Any local variables should be encapsulated by getters/setters so they
|
|
|
- //appear in the SDK properties window and can be edited.
|
|
|
- //Right-click a local variable to encapsulate it with getters and setters.
|
|
|
- private final Ray ray = new Ray(Vector3f.ZERO.clone(), new Vector3f(0, -1, 0));
|
|
|
- private final Vector3f up = new Vector3f(0, 50, 0);
|
|
|
- private final CollisionResults results = new CollisionResults();
|
|
|
- private final float offset = 0.5f;
|
|
|
- private Spatial terrain;
|
|
|
-
|
|
|
- public TerrainTrackControl() {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlUpdate(float tpf) {
|
|
|
- terrain = spatial.getParent();
|
|
|
- if (terrain != null) {
|
|
|
- ray.setOrigin(spatial.getWorldTranslation().add(up));
|
|
|
- ray.setLimit(100);
|
|
|
- results.clear();
|
|
|
- terrain.collideWith(ray, results);
|
|
|
- for (Iterator<CollisionResult> it = results.iterator(); it.hasNext(); ) {
|
|
|
- CollisionResult collisionResult = it.next();
|
|
|
- if (isTerrain(collisionResult.getGeometry()) ) {
|
|
|
- Vector3f loc = collisionResult.getContactPoint();
|
|
|
- spatial.setLocalTranslation(
|
|
|
- spatial.getLocalTranslation().setY(loc.getY() * offset));
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isTerrain(Spatial spat) {
|
|
|
- while (true) {
|
|
|
- if (spat == null) {
|
|
|
- return false;
|
|
|
- } else if ("terrain".equals(spat.getName())) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- spat = spat.getParent();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
|
- //Only needed for rendering-related operations,
|
|
|
- //not called when spatial is culled.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
- TerrainTrackControl control = new TerrainTrackControl();
|
|
|
- //TODO: copy parameters to new Control
|
|
|
- return control;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void read(JmeImporter im) throws IOException {
|
|
|
- super.read(im);
|
|
|
- InputCapsule in = im.getCapsule(this);
|
|
|
- //TODO: load properties of this Control, e.g.
|
|
|
- //this.value = in.readFloat("name", defaultValue);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void write(JmeExporter ex) throws IOException {
|
|
|
- super.write(ex);
|
|
|
- OutputCapsule out = ex.getCapsule(this);
|
|
|
- //TODO: save properties of this Control, e.g.
|
|
|
- //out.write(this.value, "name", defaultValue);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-----
|
|
|
-
|
|
|
-.AnimUpdateControl.java
|
|
|
-
|
|
|
-[source,java]
|
|
|
-----
|
|
|
-
|
|
|
-package mygame;
|
|
|
-
|
|
|
-import com.jme3.animation.AnimChannel;
|
|
|
-import com.jme3.animation.AnimControl;
|
|
|
-import com.jme3.export.InputCapsule;
|
|
|
-import com.jme3.export.JmeExporter;
|
|
|
-import com.jme3.export.JmeImporter;
|
|
|
-import com.jme3.export.OutputCapsule;
|
|
|
-import com.jme3.renderer.RenderManager;
|
|
|
-import com.jme3.renderer.ViewPort;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import com.jme3.scene.control.AbstractControl;
|
|
|
-import com.jme3.scene.control.Control;
|
|
|
-import java.io.IOException;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author normenhansen
|
|
|
- */
|
|
|
-public class AnimUpdateControl extends AbstractControl {
|
|
|
- //Any local variables should be encapsulated by getters/setters so they
|
|
|
- //appear in the SDK properties window and can be edited.
|
|
|
- //Right-click a local variable to encapsulate it with getters and setters.
|
|
|
- public static final String RUN_TOP = "RunTop";
|
|
|
- public static final String RUN_BASE = "RunBase";
|
|
|
- public static final String IDLE_TOP = "IdleTop";
|
|
|
- public static final String IDLE_BASE = "IdleBase";
|
|
|
- private AnimControl animControl;
|
|
|
- private AnimChannel torsoChannel;
|
|
|
- private AnimChannel feetChannel;
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlUpdate(float tpf) {
|
|
|
- QuixoteControl quixote = spatial.getControl(QuixoteControl.class);
|
|
|
- if (quixote != null && checkAnimControl()) {
|
|
|
- if (quixote.getSpeed() > 0) {
|
|
|
- if (!RUN_TOP.equals(torsoChannel.getAnimationName())) {
|
|
|
- torsoChannel.setAnim(RUN_TOP);
|
|
|
- }
|
|
|
- }
|
|
|
- if (!RUN_BASE.equals(feetChannel.getAnimationName())) {
|
|
|
- feetChannel.setAnim(RUN_BASE);
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (!IDLE_TOP.equals(torsoChannel.getAnimationName())) {
|
|
|
- torsoChannel.setAnim(IDLE_TOP);
|
|
|
- }
|
|
|
- if (!IDLE_BASE.equals(feetChannel.getAnimationName())) {
|
|
|
- feetChannel.setAnim(IDLE_BASE);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Checks if the animControl is available and creates channels if it is.
|
|
|
- */
|
|
|
- private boolean checkAnimControl() {
|
|
|
- AnimControl control = spatial.getControl(AnimControl.class);
|
|
|
- if (control != animControl) {
|
|
|
- this.animControl = control;
|
|
|
- if (animControl != null) {
|
|
|
- torsoChannel = animControl.createChannel();
|
|
|
- feetChannel = animControl.createChannel();
|
|
|
- }
|
|
|
- }
|
|
|
- return animControl != null;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
|
- //Only needed for rendering-related operations,
|
|
|
- //not called when spatial is culled.
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
- AnimUpdateControl control = new AnimUpdateControl();
|
|
|
- //TODO: copy parameters to new Control
|
|
|
- control.setSpatial(spatial);
|
|
|
- return control;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void read(JmeImporter im) throws IOException {
|
|
|
- super.read(im);
|
|
|
- InputCapsule in = im.getCapsule(this);
|
|
|
- //TODO: load properties of this Control, e.g.
|
|
|
- //this.value = in.readFloat("name", defaultValue);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void write(JmeExporter ex) throws IOException {
|
|
|
- super.write(ex);
|
|
|
- OutputCapsule out = ex.getCapsule(this);
|
|
|
- //TODO: save properties of this Control, e.g.
|
|
|
- //out.write(this.value, "name", defaultValue);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-----
|