OSDN Git Service

6c14032c94811ddf737759a6d20f4bf865340425
[mikumikustudio/MikuMikuStudio.git] / src / com / jme / input / ThirdPersonHandler.java
1 /*
2  * Copyright (c) 2003-2009 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 
17  *   may be used to endorse or promote products derived from this software 
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 package com.jme.input;
34
35 import java.util.HashMap;
36
37 import com.jme.input.action.InputActionEvent;
38 import com.jme.input.action.KeyInputAction;
39 import com.jme.input.thirdperson.MovementPermitter;
40 import com.jme.input.thirdperson.ThirdPersonBackwardAction;
41 import com.jme.input.thirdperson.ThirdPersonForwardAction;
42 import com.jme.input.thirdperson.ThirdPersonJoystickPlugin;
43 import com.jme.input.thirdperson.ThirdPersonLeftAction;
44 import com.jme.input.thirdperson.ThirdPersonRightAction;
45 import com.jme.input.thirdperson.ThirdPersonStrafeLeftAction;
46 import com.jme.input.thirdperson.ThirdPersonStrafeRightAction;
47 import com.jme.math.FastMath;
48 import com.jme.math.Quaternion;
49 import com.jme.math.Vector3f;
50 import com.jme.renderer.Camera;
51 import com.jme.scene.Spatial;
52
53 /**
54  * <code>ThirdPersonHandler</code> defines an InputHandler that sets input to
55  * be controlled similar to games such as Zelda Windwaker and Mario 64, etc.
56  * 
57  * @author <a href="mailto:josh@renanse.com">Joshua Slack</a>
58  * @version $Revision: 1.30 $
59  */
60
61 public class ThirdPersonHandler extends InputHandler {
62     public static final String PROP_TURNSPEED = "turnSpeed";
63     public static final String PROP_DOGRADUAL = "doGradual";
64     public static final String PROP_ROTATEONLY = "rotateOnly";
65     public static final String PROP_PERMITTER = "permitter";
66     public static final String PROP_UPVECTOR = "upVector";
67     public static final String PROP_LOCKBACKWARDS = "lockBackwards";
68     public static final String PROP_CAMERAALIGNEDMOVE = "cameraAlignedMovement";
69     public static final String PROP_STRAFETARGETALIGN = "targetAlignStrafe";
70
71     public static final String PROP_KEY_FORWARD = "fwdKey";
72     public static final String PROP_KEY_BACKWARD = "backKey";
73     public static final String PROP_KEY_LEFT = "leftKey";
74     public static final String PROP_KEY_RIGHT = "rightKey";
75     public static final String PROP_KEY_STRAFELEFT = "strfLeftKey";
76     public static final String PROP_KEY_STRAFERIGHT = "strfRightKey";
77     
78     
79     /** Default character turn speed is 1.5pi per sec. */
80     public static final float DEFAULT_TURNSPEED = 1.5f * FastMath.PI;
81
82     public static float angleEpsilon = 0.001f;
83     
84     protected float speed = 0;
85     
86     /** The Spatial we are controlling with this handler. */
87     protected Spatial targetSpatial;
88
89     /**
90      * The previous location of the target node... used to maintain where the
91      * node is before actions are run. This allows a comparison to see where the
92      * node wants to be taken.
93      */
94     protected Vector3f prevLoc = new Vector3f();
95
96     /**
97      * The previous rotation of the target node... used to maintain the node's
98      * rotation before actions are run. This is used when target aligned
99      * movement is used and strafing is done.
100      */
101     protected Quaternion prevRot = new Quaternion();
102
103     /**
104      * Stores the new location of the node after actions. used internally by
105      * update method
106      */
107     protected Vector3f loc = new Vector3f();
108
109     /**
110      * The current facing direction of the controlled target in radians in terms
111      * of relationship to the world.
112      */
113     protected float faceAngle;
114
115     /**
116      * How fast the character can turn per second. Used when doGradualRotation
117      * is set to true.
118      */
119     protected float turnSpeed = DEFAULT_TURNSPEED;
120
121     /**
122      * When true, the controlled target will do turns by moving forward and
123      * turning at the same time. When false, a turn will cause immediate
124      * rotation to the given angle.
125      */
126     protected boolean doGradualRotation = true;
127
128     /**
129      * When not null, gives a means for denying movement to the controller. See
130      * MovementPermitter javadoc for more.
131      */
132     protected MovementPermitter permitter;
133
134     /** World up vector.  Currently 0,1,0 is the only guarenteed value to work. */
135     protected Vector3f upVector = new Vector3f(0, 1, 0);
136
137     /** An internal vector used for calculations to prevent object creation. */
138     protected Vector3f calcVector = new Vector3f();
139
140     /** The camera this handler uses for determining action movement. */
141     protected Camera camera;
142     
143     /**
144      * if true, backwards movement will not cause the target to rotate around to
145      * point backwards. (useful for vehicle movement) Default is false.
146      */
147     protected boolean lockBackwards;
148     
149     /**
150      * if true, strafe movements will always be target aligned, even if other
151      * movement is camera aligned.  Default is false.
152      */
153     protected boolean strafeAlignTarget;
154
155     /**
156      * if true, left and right keys will rotate the target instead of moving them.
157      * Default is false.
158      */
159     protected boolean rotateOnly;
160     
161     /**
162      * if true, movements of the character are in relation to the current camera
163      * view. If false, they are in relation to the current target's facing
164      * vector. Default is true.
165      */
166     protected boolean cameraAlignedMovement;
167     
168     /**
169      * internally used boolean for denoting that a backwards action is currently
170      * being performed.
171      */
172     protected boolean walkingBackwards;
173     
174     /**
175      * internally used boolean for denoting that a forward action is currently
176      * being performed.
177      */
178     protected boolean walkingForward;
179     
180     /**
181      * internally used boolean for denoting that a turning action is currently
182      * being performed.
183      */
184     protected boolean nowTurning;
185     
186     /**
187      * internally used boolean for denoting that a turning action is currently
188      * being performed.
189      */
190     protected boolean nowStrafing;
191
192     protected ThirdPersonJoystickPlugin plugin = null;
193     
194     protected KeyInputAction actionForward;
195     protected KeyInputAction actionBack;
196     protected KeyInputAction actionRight;
197     protected KeyInputAction actionLeft;
198     protected KeyInputAction actionStrafeRight;
199     protected KeyInputAction actionStrafeLeft;
200
201     /**
202      * Basic constructor for the ThirdPersonHandler. Sets all non specified args
203      * to their defaults.
204      * 
205      * @param target
206      *            the target to move
207      * @param cam
208      *            the camera for movements to be in relation to
209      */
210     public ThirdPersonHandler(Spatial target, Camera cam) {
211         this(target, cam, null);
212     }
213     
214     /**
215      * Full constructor for the ThirdPersonHandler. Properties in the props arg
216      * will be used to set handler fields if set, otherwise default values are
217      * used.
218      * 
219      * @param target
220      *            the target to move
221      * @param cam
222      *            the camera for movements to be in relation to
223      * @param props
224      *            a hashmap of properties used to set handler characteristics
225      *            where the key is one of this class's static PROP_XXXX fields.
226      */
227     public ThirdPersonHandler(Spatial target, Camera cam, HashMap<String, Object> props) {
228         this.targetSpatial = target;
229         this.camera = cam;
230
231         updateProperties(props);
232         setActions();
233     }
234
235     /**
236      * 
237      * <code>setProperties</code> sets up class fields from the given hashmap.
238      * It also calls updateKeyBindings for you.
239      * 
240      * @param props
241      */
242     public void updateProperties(HashMap<String, Object> props) {
243         turnSpeed = getFloatProp(props, PROP_TURNSPEED, DEFAULT_TURNSPEED);
244         doGradualRotation = getBooleanProp(props, PROP_DOGRADUAL, true);
245         lockBackwards = getBooleanProp(props, PROP_LOCKBACKWARDS, false);
246         strafeAlignTarget = getBooleanProp(props, PROP_STRAFETARGETALIGN, false);
247         cameraAlignedMovement = getBooleanProp(props, PROP_CAMERAALIGNEDMOVE, true);
248         rotateOnly = getBooleanProp(props, PROP_ROTATEONLY, false);
249         permitter = (MovementPermitter)getObjectProp(props, PROP_PERMITTER, null);
250         upVector = (Vector3f)getObjectProp(props, PROP_UPVECTOR, Vector3f.UNIT_Y.clone());
251         updateKeyBindings(props);
252     }
253
254     /**
255      * 
256      * <code>updateKeyBindings</code> allows a user to update the keys mapped to the various actions.
257      * 
258      * @param props
259      */
260     public void updateKeyBindings(HashMap<String, Object> props) {
261         KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
262         keyboard.set(PROP_KEY_FORWARD, getIntProp(props, PROP_KEY_FORWARD, KeyInput.KEY_W));
263         keyboard.set(PROP_KEY_BACKWARD, getIntProp(props, PROP_KEY_BACKWARD, KeyInput.KEY_S));
264         keyboard.set(PROP_KEY_LEFT, getIntProp(props, PROP_KEY_LEFT, KeyInput.KEY_A));
265         keyboard.set(PROP_KEY_RIGHT, getIntProp(props, PROP_KEY_RIGHT, KeyInput.KEY_D));
266         keyboard.set(PROP_KEY_STRAFELEFT, getIntProp(props, PROP_KEY_STRAFELEFT, KeyInput.KEY_Q));
267         keyboard.set(PROP_KEY_STRAFERIGHT, getIntProp(props, PROP_KEY_STRAFERIGHT, KeyInput.KEY_E));        
268     }
269
270     /**
271      * 
272      * <code>setActions</code> sets the keyboard actions with the
273      * corresponding key command.
274      *
275      */
276     protected void setActions() {
277         actionForward = new ThirdPersonForwardAction( this, 100f );
278         actionBack = new ThirdPersonBackwardAction( this, 100f );
279         actionRight = new ThirdPersonRightAction( this, 100f );
280         actionLeft = new ThirdPersonLeftAction( this, 100f );
281         actionStrafeRight = new ThirdPersonStrafeRightAction( this, 100f );
282         actionStrafeLeft = new ThirdPersonStrafeLeftAction( this, 100f );
283         addAction( actionForward, PROP_KEY_FORWARD, true );
284         addAction( actionBack, PROP_KEY_BACKWARD, true );
285         addAction( actionRight, PROP_KEY_RIGHT, true );
286         addAction( actionLeft, PROP_KEY_LEFT, true );
287         addAction( actionStrafeRight, PROP_KEY_STRAFERIGHT, true );
288         addAction( actionStrafeLeft, PROP_KEY_STRAFELEFT, true );
289     }
290
291     /**
292      * <code>update</code> updates the position and rotation of the target
293      * based on the movement requested by the user.
294      * 
295      * @param time
296      * @see com.jme.input.InputHandler#update(float)
297      */
298     public void update(float time) {
299         if ( !isEnabled() ) return;
300
301         walkingForward = false;
302         walkingBackwards = false;
303         nowTurning = false;
304         nowStrafing = false;
305
306         prevLoc.set(targetSpatial.getLocalTranslation());
307         loc.set(prevLoc);
308
309         doInputUpdate(time);
310         if (walkingBackwards && walkingForward && !nowStrafing && !nowTurning) {
311             targetSpatial.getLocalTranslation().set(prevLoc);
312             return;
313         }
314         targetSpatial.getLocalTranslation().subtract(loc, loc);
315         if (!loc.equals(Vector3f.ZERO)) {
316             float distance = loc.length();
317             if (distance != 0 && distance != 1.0f)
318                 loc.divideLocal(distance); // this is same as normalizeLocal.
319             
320             float actAngle = 0;
321             targetSpatial.getLocalRotation().getRotationColumn(2, calcVector);
322             if (upVector.y == 1) {
323                 actAngle = FastMath.atan2(loc.z, loc.x);
324                 if (!nowTurning && !nowStrafing) {
325                     faceAngle = FastMath.atan2(calcVector.z, calcVector.x);
326                 }
327             } else if (upVector.x == 1) {
328                 actAngle = FastMath.atan2(loc.z, loc.y);
329                 if (!nowTurning && !nowStrafing)
330                     faceAngle = FastMath.atan2(calcVector.z, calcVector.y);
331             } else if (upVector.z == 1) {
332                 actAngle = FastMath.atan2(loc.x, loc.y) - FastMath.HALF_PI;
333                 if (!nowTurning && !nowStrafing)
334                     faceAngle = FastMath.atan2(calcVector.x, calcVector.y) - FastMath.HALF_PI;
335             }
336             
337             float oldFace = faceAngle;
338             calcFaceAngle(actAngle, time);
339             if (nowStrafing) {
340                 faceAngle = actAngle;
341                 prevRot.set(targetSpatial.getLocalRotation());
342             }
343             targetSpatial.getLocalRotation().fromAngleNormalAxis(-(faceAngle - FastMath.HALF_PI), upVector);
344             targetSpatial.getLocalRotation().getRotationColumn(2, calcVector).multLocal(distance);
345
346             if (nowStrafing) {
347                 if (!strafeAlignTarget && cameraAlignedMovement) {
348                     if (upVector.y == 1) {
349                         faceAngle = FastMath.atan2(camera.getDirection().z, camera.getDirection().x);
350                     } else if (upVector.x == 1) {
351                         faceAngle = FastMath.atan2(camera.getDirection().z, camera.getDirection().y);
352                     } else if (upVector.z == 1) {
353                         faceAngle = FastMath.atan2(camera.getDirection().x, camera.getDirection().y) - FastMath.HALF_PI;
354                     }
355                     targetSpatial.getLocalRotation().fromAngleNormalAxis(-faceAngle, upVector);
356                 } else {
357                     targetSpatial.getLocalRotation().set(prevRot);
358                     faceAngle = oldFace;
359                 }
360             }
361
362             targetSpatial.getLocalTranslation().set(prevLoc);
363             if (lockBackwards && walkingBackwards && !nowStrafing)
364                 targetSpatial.getLocalTranslation().subtractLocal(calcVector);
365             else if (rotateOnly && nowTurning && !walkingBackwards && !walkingForward)
366                 ; // no translation
367             else 
368                 targetSpatial.getLocalTranslation().addLocal(calcVector);
369         }
370     }
371
372     protected void doInputUpdate(float time) {
373         super.update(time);
374         updateFromJoystick(time);
375     }
376
377     protected void updateFromJoystick(float time) {
378         if (plugin == null) return;
379         float xAmnt = plugin.getJoystick().getAxisValue(plugin.getXAxis());
380         float yAmnt = plugin.getJoystick().getAxisValue(plugin.getYAxis());
381         
382         InputActionEvent evt = new InputActionEvent();
383         if (xAmnt > 0) {
384             evt.setTime(time*xAmnt);
385             actionRight.performAction(evt);
386         } else if (xAmnt < 0) {
387             evt.setTime(time*-xAmnt);
388             actionLeft.performAction(evt);            
389         }
390         
391         if (yAmnt > 0) {
392             evt.setTime(time*yAmnt);
393             actionBack.performAction(evt);
394         } else if (yAmnt < 0) {
395             evt.setTime(time*-yAmnt);
396             actionForward.performAction(evt);            
397         }
398     }
399
400     /**
401      * <code>calcFaceAngle</code>
402      * @param actAngle
403      * @param time
404      */
405     protected void calcFaceAngle(float actAngle, float time) {
406         if (doGradualRotation) {
407             faceAngle = FastMath.normalize(faceAngle, -FastMath.PI, FastMath.PI);
408             float oldAct = actAngle;
409
410             // Check the difference between action angle and current facing angle.
411             actAngle -= faceAngle;
412             actAngle = FastMath.normalize(actAngle, -FastMath.PI, FastMath.PI);
413             if (FastMath.abs(actAngle) <= angleEpsilon) {
414                 return;
415             }
416
417             boolean above = faceAngle > oldAct;
418             if (lockBackwards && walkingBackwards) {
419                 // update faceangle rotation towards action angle
420                 if (actAngle > angleEpsilon && actAngle < FastMath.PI)
421                     faceAngle -= time * turnSpeed;
422                 else if (actAngle < -angleEpsilon || actAngle > FastMath.PI)
423                     faceAngle += time * turnSpeed;
424             } else {
425                 // update faceangle rotation towards action angle
426                 if (actAngle > angleEpsilon && actAngle < FastMath.PI) {
427                     faceAngle += time * turnSpeed;
428                     if (!above && faceAngle > oldAct) faceAngle = oldAct;
429                 } else if (actAngle < -angleEpsilon || actAngle > FastMath.PI) {
430                     faceAngle -= time * turnSpeed;
431                     if (above && faceAngle < oldAct) faceAngle = oldAct;
432                 }
433             }
434         } else {
435             if (lockBackwards && walkingBackwards)
436                 faceAngle = FastMath.PI + actAngle;
437             else
438                 faceAngle = actAngle;
439         }
440     }
441
442     /**
443      * @return Returns the turnSpeed.
444      */
445     public float getTurnSpeed() {
446         return turnSpeed;
447     }
448
449     /**
450      * @param turnSpeed
451      *            The turnSpeed to set.
452      */
453     public void setTurnSpeed(float turnSpeed) {
454         this.turnSpeed = turnSpeed;
455     }
456
457     /**
458      * @return Returns the upAngle.
459      */
460     public Vector3f getUpVector() {
461         return upVector;
462     }
463
464     /**
465      * @param upAngle
466      *            The upAngle to set (as copy)
467      */
468     public void setUpVector(Vector3f upAngle) {
469         this.upVector.set(upAngle);
470     }
471
472     /**
473      * @return Returns the faceAngle (in radians)
474      */
475     public float getFaceAngle() {
476         return faceAngle;
477     }
478
479     /**
480      * @return Returns the doGradualRotation.
481      */
482     public boolean isDoGradualRotation() {
483         return doGradualRotation;
484     }
485
486     /**
487      * @param doGradualRotation
488      *            The doGradualRotation to set.
489      */
490     public void setDoGradualRotation(boolean doGradualRotation) {
491         this.doGradualRotation = doGradualRotation;
492     }
493
494     public MovementPermitter getPermitter() {
495         return permitter;
496     }
497
498     public Spatial getTarget() {
499         return targetSpatial;
500     }
501
502     public void setTarget(Spatial target) {
503         this.targetSpatial = target;
504     }
505
506     public Camera getCamera() {
507         return camera;
508     }
509
510     public void setStrafeAlignTarget(boolean b) {
511         strafeAlignTarget = b;
512     }
513
514     public boolean isStrafeAlignTarget() {
515         return strafeAlignTarget;
516     }
517
518     public void setLockBackwards(boolean b) {
519         lockBackwards = b;
520     }
521
522     public boolean isLockBackwards() {
523         return lockBackwards;
524     }
525
526     public void setRotateOnly(boolean b) {
527         rotateOnly = b;
528     }
529
530     public boolean isRotateOnly() {
531         return rotateOnly;
532     }
533
534     public void setCameraAlignedMovement(boolean b) {
535         cameraAlignedMovement = b;
536     }
537
538     public boolean isCameraAlignedMovement() {
539         return cameraAlignedMovement;
540     }
541
542     /**
543      * Internal method used to let the handler know that the target is currently
544      * moving forward (via use of the forward key.)
545      * 
546      * @param forward
547      */
548     public void setGoingForward(boolean forward) {
549         walkingForward = forward;
550     }
551
552     /**
553      * Internal method used to let the handler know that the target is currently
554      * moving backwards (via use of the back key.)
555      * 
556      * @param backwards
557      */
558     public void setGoingBackwards(boolean backwards) {
559         walkingBackwards = backwards;
560     }
561
562     /**
563      * Internal method used to let the handler know that the target is currently
564      * turning/moving left/right (via use of the left/right keys.)
565      * 
566      * @param turning
567      */
568     public void setTurning(boolean turning) {
569         nowTurning = turning;
570     }
571
572     /**
573      * Internal method used to let the handler know that the target is currently
574      * strafing left/right (via use of the strafe left/right keys.)
575      * 
576      * @param strafe
577      */
578     public void setStrafing(boolean strafe) {
579         nowStrafing = strafe;
580     }
581
582     /**
583      * @return true if last update of handler included a turn left/right action.
584      */
585     public boolean isNowTurning() {
586         return nowTurning;
587     }
588
589     /**
590      * @return true if last update of handler included a walk backwards action.
591      */
592     public boolean isWalkingBackwards() {
593         return walkingBackwards;
594     }
595
596     /**
597      * @return true if last update of handler included a walk forward action.
598      */
599     public boolean isWalkingForward() {
600         return walkingForward;
601     }
602
603     /**
604      * @return true if last update of handler included a walk forward action.
605      */
606     public boolean isStrafing() {
607         return nowStrafing;
608     }
609
610     public void setActionSpeed(float speed) {
611         super.setActionSpeed(speed);
612         this.speed = speed;
613     }
614     
615     public float getSpeed() {
616         return speed;
617     }
618
619     /**
620      * @return Returns the joystick plugin or null if not set.
621      */
622     public ThirdPersonJoystickPlugin getJoystickPlugin() {
623         return plugin;
624     }
625
626     /**
627      * @param plugin The joystick plugin to set.
628      */
629     public void setJoystickPlugin(ThirdPersonJoystickPlugin plugin) {
630         this.plugin = plugin;
631     }
632 }