From 28675bac6d5fb3bb84ed14cd601f811997f615e4 Mon Sep 17 00:00:00 2001 From: "Chris@Chris-HP" Date: Fri, 17 May 2013 09:33:08 +0200 Subject: [PATCH] Implement PlayerShootControl, PlayerMouseControl and WeaponProjectileControl --- ShootingTest/MANIFEST.MF | 1 + ShootingTest/build.xml | 76 + ShootingTest/master-application.jnlp | 22 + ShootingTest/nbproject/assets-impl.xml | 15 + ShootingTest/nbproject/build-impl.xml | 1382 +++++++++++++++++ ShootingTest/nbproject/genfiles.properties | 8 + ShootingTest/nbproject/project.properties | 76 + ShootingTest/nbproject/project.xml | 18 + ShootingTest/src/mygame/Main.java | 62 + .../src/mygame/PlayerMouseControl.java | 120 ++ .../src/mygame/PlayerShootControl.java | 74 + .../src/mygame/WeaponProjectileControl.java | 109 ++ 12 files changed, 1963 insertions(+) create mode 100644 ShootingTest/MANIFEST.MF create mode 100644 ShootingTest/build.xml create mode 100644 ShootingTest/master-application.jnlp create mode 100644 ShootingTest/nbproject/assets-impl.xml create mode 100644 ShootingTest/nbproject/build-impl.xml create mode 100644 ShootingTest/nbproject/genfiles.properties create mode 100644 ShootingTest/nbproject/project.properties create mode 100644 ShootingTest/nbproject/project.xml create mode 100644 ShootingTest/src/mygame/Main.java create mode 100644 ShootingTest/src/mygame/PlayerMouseControl.java create mode 100644 ShootingTest/src/mygame/PlayerShootControl.java create mode 100644 ShootingTest/src/mygame/WeaponProjectileControl.java diff --git a/ShootingTest/MANIFEST.MF b/ShootingTest/MANIFEST.MF new file mode 100644 index 0000000..7e1ad4b --- /dev/null +++ b/ShootingTest/MANIFEST.MF @@ -0,0 +1 @@ +X-Comment: Created with jMonkeyPlatform diff --git a/ShootingTest/build.xml b/ShootingTest/build.xml new file mode 100644 index 0000000..01bd9fe --- /dev/null +++ b/ShootingTest/build.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + Builds, tests, and runs the project BasicGameTemplate. + + + + + diff --git a/ShootingTest/master-application.jnlp b/ShootingTest/master-application.jnlp new file mode 100644 index 0000000..49cbb12 --- /dev/null +++ b/ShootingTest/master-application.jnlp @@ -0,0 +1,22 @@ + + + ${APPLICATION.TITLE} + ${APPLICATION.VENDOR} + + ${APPLICATION.DESC} + ${APPLICATION.DESC.SHORT} + + + + + + + + + + + + + + + diff --git a/ShootingTest/nbproject/assets-impl.xml b/ShootingTest/nbproject/assets-impl.xml new file mode 100644 index 0000000..0a47d8d --- /dev/null +++ b/ShootingTest/nbproject/assets-impl.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/ShootingTest/nbproject/build-impl.xml b/ShootingTest/nbproject/build-impl.xml new file mode 100644 index 0000000..1ad24fe --- /dev/null +++ b/ShootingTest/nbproject/build-impl.xml @@ -0,0 +1,1382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShootingTest/nbproject/genfiles.properties b/ShootingTest/nbproject/genfiles.properties new file mode 100644 index 0000000..a895c7f --- /dev/null +++ b/ShootingTest/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=94bf7c61 +build.xml.script.CRC32=79a29eb7 +build.xml.stylesheet.CRC32=958a1d3e@1.32.1.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=99ad4fd9 +nbproject/build-impl.xml.script.CRC32=ed28b3b3 +nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff --git a/ShootingTest/nbproject/project.properties b/ShootingTest/nbproject/project.properties new file mode 100644 index 0000000..4b0191f --- /dev/null +++ b/ShootingTest/nbproject/project.properties @@ -0,0 +1,76 @@ +application.title=MyGame +application.vendor=MyCompany +assets.jar.name=assets.jar +assets.excludes=**/*.j3odata,**/*.mesh\.xml,**/*.skeleton\.xml,**/*.scene,**/*.material,**/*.obj,**/*.mtl,**/*.blend +assets.folder.name=assets +assets.compress=true +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +compile.on.save=true +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/${application.title}.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath=\ + ${libs.jme3.classpath}:\ + ${libs.jme3-libraries.classpath} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" +jnlp.codebase.type=local +jnlp.descriptor=application +jnlp.enabled=false +jnlp.offline-allowed=false +jnlp.signed=false +main.class=mygame.Main +meta.inf.dir=${src.dir}/META-INF +manifest.file=MANIFEST.MF +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${assets.folder.name} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src diff --git a/ShootingTest/nbproject/project.xml b/ShootingTest/nbproject/project.xml new file mode 100644 index 0000000..933a1ba --- /dev/null +++ b/ShootingTest/nbproject/project.xml @@ -0,0 +1,18 @@ + + + org.netbeans.modules.java.j2seproject + + + + + + + + ShootingTest + + + + + + + diff --git a/ShootingTest/src/mygame/Main.java b/ShootingTest/src/mygame/Main.java new file mode 100644 index 0000000..5053106 --- /dev/null +++ b/ShootingTest/src/mygame/Main.java @@ -0,0 +1,62 @@ +package mygame; + +import com.jme3.app.DebugKeysAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.app.StatsAppState; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; + +/** + * @author Snowsun + */ +public class Main extends SimpleApplication { + + private Spatial player; + private PlayerMouseControl playerMouseControl; + private PlayerShootControl playerShootControl; + private WeaponProjectileControl weaponProjectileControl; + + public Main() { + super(new StatsAppState(), new DebugKeysAppState()); + } + + public static void main(String[] args) { + Main app = new Main(); + app.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(Vector3f.ZERO, 1, 1, 1); + player = new Geometry("Player",b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Blue); + player.setMaterial(mat); + + inputManager.setCursorVisible(false); + playerMouseControl = new PlayerMouseControl(inputManager,cam); + player.addControl(playerMouseControl); + playerMouseControl.setSpeed(2); + + playerShootControl = new PlayerShootControl(inputManager); + player.addControl(playerShootControl); + + weaponProjectileControl = new WeaponProjectileControl(assetManager, rootNode, inputManager); + player.addControl(weaponProjectileControl); + + rootNode.attachChild(player); + } + + @Override + public void simpleUpdate(float tpf) { + } + + @Override + public void simpleRender(RenderManager rm) { + } +} diff --git a/ShootingTest/src/mygame/PlayerMouseControl.java b/ShootingTest/src/mygame/PlayerMouseControl.java new file mode 100644 index 0000000..bf391a9 --- /dev/null +++ b/ShootingTest/src/mygame/PlayerMouseControl.java @@ -0,0 +1,120 @@ +package mygame; + +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.AnalogListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseAxisTrigger; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +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 Snowsun + */ +public class PlayerMouseControl extends AbstractControl implements AnalogListener { + + private float speed; + private InputManager inputManager; + private Camera cam; + private Vector3f initialUpVec; + + public PlayerMouseControl(InputManager inputManager, Camera cam) { + this.inputManager = inputManager; + this.cam = cam; + initMappings(); + } + + @Override + public void setSpatial(Spatial spatial) { + super.setSpatial(spatial); + initialUpVec = spatial.getLocalRotation().getRotationColumn(1).clone(); + } + + @Override + protected void controlUpdate(float tpf) { + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + } + + public Control cloneForSpatial(Spatial spatial) { + PlayerMouseControl control = new PlayerMouseControl(inputManager,cam); + spatial.addControl(control); + return control; + } + + public void setSpeed(float speed) { + this.speed = speed; + } + + public float getSpeed() { + return speed; + } + + public void onAnalog(String name, float value, float tpf) { + if (name.equals("PLAYER_Left")) { + rotateCamera(value, initialUpVec); + } else if (name.equals("PLAYER_Right")) { + rotateCamera(-value, initialUpVec); + } else if (name.equals("PLAYER_Up")) { + rotateCamera(-value, spatial.getLocalRotation().getRotationColumn(0)); + } else if (name.equals("PLAYER_Down")) { + rotateCamera(value, spatial.getLocalRotation().getRotationColumn(0)); + } + } + + private void initMappings() { + inputManager.addMapping("PLAYER_Left", new MouseAxisTrigger(MouseInput.AXIS_X, true), + new KeyTrigger(KeyInput.KEY_LEFT)); + + inputManager.addMapping("PLAYER_Right", new MouseAxisTrigger(MouseInput.AXIS_X, false), + new KeyTrigger(KeyInput.KEY_RIGHT)); + + inputManager.addMapping("PLAYER_Up", new MouseAxisTrigger(MouseInput.AXIS_Y, false), + new KeyTrigger(KeyInput.KEY_UP)); + + inputManager.addMapping("PLAYER_Down", new MouseAxisTrigger(MouseInput.AXIS_Y, true), + new KeyTrigger(KeyInput.KEY_DOWN)); + inputManager.addListener(this, new String[]{"PLAYER_Left","PLAYER_Right","PLAYER_Up","PLAYER_Down"}); + } + + private void rotateCamera(float value, Vector3f axis) { + Matrix3f mat = new Matrix3f(); + mat.fromAngleNormalAxis(speed * value, axis); + + + Vector3f up = spatial.getLocalRotation().getRotationColumn(1); + Vector3f left = spatial.getLocalRotation().getRotationColumn(0); + Vector3f dir = spatial.getLocalRotation().getRotationColumn(2); + + mat.mult(up, up); + mat.mult(left, left); + mat.mult(dir, dir); + + Quaternion q = new Quaternion(); + q.fromAxes(left, up, dir); + q.normalizeLocal(); + + float[] angles = q.toAngles(null); + if (angles[0] < -1.4f) { + angles[0] = -1.4f; + spatial.setLocalRotation(new Quaternion(angles)); + } else if (angles[0] > 1.4f) { + angles[0] = 1.4f; + spatial.setLocalRotation(new Quaternion(angles)); + } else { + spatial.setLocalRotation(q); + } + } + +} diff --git a/ShootingTest/src/mygame/PlayerShootControl.java b/ShootingTest/src/mygame/PlayerShootControl.java new file mode 100644 index 0000000..b09da7e --- /dev/null +++ b/ShootingTest/src/mygame/PlayerShootControl.java @@ -0,0 +1,74 @@ +package mygame; + +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +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 Snowsun + */ +public class PlayerShootControl extends AbstractControl implements ActionListener { + + private InputManager inputManager; + private final float maxTimeToOverheat = 3f; + private float timeToOverheat = maxTimeToOverheat; + private boolean isShooting = false; + + public PlayerShootControl(InputManager inputmanager) { + this.inputManager = inputmanager; + initMappings(); + } + + @Override + protected void controlUpdate(float tpf) { + if (isShooting) { + timeToOverheat -= tpf; + if (timeToOverheat <= 0) { + isShooting = false; + } + } + } + + @Override + public void setSpatial(Spatial spatial) { + super.setSpatial(spatial); + } + + public boolean getIsShooting() { + return this.isShooting; + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + } + + public Control cloneForSpatial(Spatial spatial) { + PlayerShootControl control = new PlayerShootControl(inputManager); + spatial.addControl(control); + return control; + } + + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("PLAYER_Mouse_Left_Click") && isPressed) { + isShooting = true; + } else if (name.equals("PLAYER_Mouse_Left_Click") && !isPressed) { + isShooting = false; + timeToOverheat = maxTimeToOverheat; + } + } + + private void initMappings() { + inputManager.addMapping("PLAYER_Mouse_Left_Click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT), + new KeyTrigger(KeyInput.KEY_DOWN)); + inputManager.addListener(this, new String[]{"PLAYER_Mouse_Left_Click"}); + } +} diff --git a/ShootingTest/src/mygame/WeaponProjectileControl.java b/ShootingTest/src/mygame/WeaponProjectileControl.java new file mode 100644 index 0000000..0391e9d --- /dev/null +++ b/ShootingTest/src/mygame/WeaponProjectileControl.java @@ -0,0 +1,109 @@ +package mygame; + +import com.jme3.asset.AssetManager; +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; +import com.jme3.scene.control.Control; +import com.jme3.scene.shape.Box; + +/** + * + * @author Snowsun + */ +public class WeaponProjectileControl extends AbstractControl implements ActionListener{ + + private Spatial projectile; + private AssetManager assetManager; + private Node rootNode; + private Vector3f location; + private InputManager inputManager; + + public WeaponProjectileControl(AssetManager assetManager, Node rootNode, InputManager inputManager) { + this.assetManager = assetManager; + this.rootNode = rootNode; + this.inputManager = inputManager; + initMappings(); + } + + @Override + public void setSpatial(Spatial spatial) { + super.setSpatial(spatial); + location = spatial.getLocalTranslation(); + initProjectile(); + } + + @Override + protected void controlUpdate(float tpf) { + if(spatial.getControl(PlayerShootControl.class).getIsShooting()) { + shoot(); + } else { + rootNode.detachChild(projectile); + } + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + } + + public Control cloneForSpatial(Spatial spatial) { + WeaponProjectileControl control = new WeaponProjectileControl(assetManager, rootNode, inputManager); + spatial.addControl(control); + return control; + } + + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("PLAYER_Mouse_Left_Click") && isPressed) { + rootNode.attachChild(projectile); + } else if (name.equals("PLAYER_Mouse_Left_Click") && !isPressed) { + rootNode.detachChild(projectile); + } + } + + private void initMappings() { + inputManager.addMapping("PLAYER_Mouse_Left_Click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT), + new KeyTrigger(KeyInput.KEY_DOWN)); + inputManager.addListener(this, new String[]{"PLAYER_Mouse_Left_Click"}); + } + + private void initProjectile() { + Box box = new Box(Vector3f.ZERO, 1, 1, 2); + projectile = new Geometry("Projectile", box); + projectile.setLocalTranslation(spatial.getLocalTranslation()); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Red); + projectile.setMaterial(mat); + } + + /* + * Fires a projectile + */ + private void shoot() { + projectile.setLocalTranslation(location.add(0, 0, -4)); + + Vector3f vectorDifference = new Vector3f(spatial.getLocalTranslation().subtract(spatial.getWorldTranslation())); + projectile.setLocalTranslation(vectorDifference.addLocal(spatial.getLocalTranslation())); + + Quaternion worldDiff = new Quaternion(spatial.getLocalRotation().subtract(spatial.getWorldRotation())); + projectile.setLocalRotation(worldDiff.addLocal(spatial.getLocalRotation())); + + projectile.move(spatial.getLocalRotation().getRotationColumn(2).mult(-3f)); + projectile.move(spatial.getLocalRotation().getRotationColumn(1).mult(0f)); + projectile.move(spatial.getLocalRotation().getRotationColumn(0).mult(0)); + projectile.rotate(0f, FastMath.PI, 0); + } +}