📄 keybehavior.java
字号:
// KeyBehavior.java
// Andrew Davison, August 2003, dandrew@ratree.psu.ac.th
/* Similar to KeyBehavior in FractalLand3D/
Arrow functionality:
* forwards, backwards, left, right, up, down, turn left/right
* where // prints the user's position in land coordinates
The major change is that a move does not _immediately_ affect
the user's height position on the terrain. The height calculation
is delegated to the HeightFinder object, which may take 1-2 secs
to obtain the height through picking. In the meantime, KeyBehavior
continues to use the old height position.
This means that the user can move inside mountains, but the user's
height will eventually be corrected.
*/
import java.awt.AWTEvent;
import java.awt.event.*;
import java.util.Enumeration;
import java.text.DecimalFormat;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.vp.*;
public class KeyBehavior extends ViewPlatformBehavior
{
private static final double ROT_AMT = Math.PI / 36.0; // 5 degrees
private static final double MOVE_STEP = 0.2; // was 0.2
private static final double USER_HEIGHT = 0.5; // of head above the floor
// hardwired movement vectors
private static final Vector3d FWD = new Vector3d(0,0,-MOVE_STEP);
private static final Vector3d BACK = new Vector3d(0,0,MOVE_STEP);
private static final Vector3d LEFT = new Vector3d(-MOVE_STEP,0,0);
private static final Vector3d RIGHT = new Vector3d(MOVE_STEP,0,0);
private static final Vector3d UP = new Vector3d(0,MOVE_STEP,0);
private static final Vector3d DOWN = new Vector3d(0,-MOVE_STEP,0);
// key names
private int forwardKey = KeyEvent.VK_UP;
private int backKey = KeyEvent.VK_DOWN;
private int leftKey = KeyEvent.VK_LEFT;
private int rightKey = KeyEvent.VK_RIGHT;
private int whereKey = KeyEvent.VK_W; // 'where' button
private WakeupCondition keyPress;
private Landscape land;
private HeightFinder hf; // HeightFinder processes height requests
private double currLandHeight; // floor height at current position (in world coords)
private int zOffset; // used when moving up/down
private DecimalFormat df; // for simpler output
// for repeated calcs
private Transform3D t3d = new Transform3D();
private Transform3D toMove = new Transform3D();
private Transform3D toRot = new Transform3D();
private Vector3d trans = new Vector3d();
public KeyBehavior(Landscape ld, TransformGroup steerTG)
{
land = ld;
zOffset = 0; // user is standing on the floor at the start
initViewPosition(steerTG);
df = new DecimalFormat("0.###"); // 3 dp
hf = new HeightFinder(land, this);
hf.start();
keyPress = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
} // end of KeyBehavior()
private void initViewPosition(TransformGroup steerTG)
// place viewpoint at starting position, facing into scene
{
Vector3d startPosn = land.getOriginVec();
// System.out.println("startPosn: " + startPosn);
currLandHeight = startPosn.y; // store current floor height
startPosn.y += USER_HEIGHT; // add user's height
steerTG.getTransform(t3d);
t3d.setTranslation(startPosn); // no targetTG yet, so use steerTG
steerTG.setTransform(t3d);
} // end of initViewPosition()
public void initialize()
{ wakeupOn( keyPress ); }
public void processStimulus(Enumeration criteria)
// respond to a keypress
{
WakeupCriterion wakeup;
AWTEvent[] event;
while( criteria.hasMoreElements() ) {
wakeup = (WakeupCriterion) criteria.nextElement();
if( wakeup instanceof WakeupOnAWTEvent ) {
event = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
for( int i = 0; i < event.length; i++ ) {
if( event[i].getID() == KeyEvent.KEY_PRESSED )
processKeyEvent((KeyEvent)event[i]);
}
}
}
wakeupOn( keyPress );
} // end of processStimulus()
private void processKeyEvent(KeyEvent eventKey)
{
int keyCode = eventKey.getKeyCode();
// System.out.println(keyCode);
if( eventKey.isAltDown() ) // key + <alt>
altMove(keyCode);
else
standardMove(keyCode);
} // end of processKeyEvent()
private void standardMove(int keycode)
/* viewer moves forward or backward;
rotate left or right. Also 'w' for where*/
{ if(keycode == forwardKey)
moveBy(FWD);
else if(keycode == backKey)
moveBy(BACK);
else if(keycode == leftKey)
rotateY(ROT_AMT);
else if(keycode == rightKey)
rotateY(-ROT_AMT);
else if (keycode == whereKey) // new key
printLandLocation();
} // end of standardMove()
private void altMove(int keycode)
// moves viewer up or down, left or right
{ if(keycode == forwardKey) {
doMove(UP); // no testing using moveBy()
zOffset++;
}
else if(keycode == backKey) {
if (zOffset > 0) {
doMove(DOWN); // no testing using moveBy()
zOffset--;
}
}
else if(keycode == leftKey)
moveBy(LEFT);
else if(keycode == rightKey)
moveBy(RIGHT);
} // end of altMove()
// ----------------------- moves ----------------------------
private void moveBy(Vector3d theMove)
/* _Eventually_ calculate the next position on the floor (x,?,z).
First, do a quick test to see if the move
will be within the land boundaries.
Then, _request_ the floor height for the given (x,z) position.
Don't wait for an answer (which may take 1-2 secs). Instead,
continue with the current floor height, which means a move
with no change in the y-axis.
The height request is sent to the HeightFinder object, which will
query the landscape through picking. When it has an answer,
it will call KeyBehavior's adjustHeight().
This means that the user may walk through the sides of
mountains, but his/her y-position will *eventually* be
adjusted.
HeightFinder is coded so that when Keybehavior sends a
request, it will replace any previously pending request.
*/
{
// System.out.println("theMove: " + theMove);
Vector3d nextLoc = tryMove(theMove); // next (x,?,z) user position
if (!land.inLandscape(nextLoc.x, nextLoc.z)) // if not on landscape
return;
hf.requestMoveHeight(nextLoc); // send height request to HeightFinder
Vector3d actualMove = new Vector3d(theMove.x, 0, theMove.z); // no y-axis change
doMove(actualMove);
} // end of moveBy()
public void adjustHeight(double newHeight)
/* Called by HeightFinder to adjust the viewer's y-axis
position. It is synchronized, so that it won't interfere
with a call to moveBy() which also adjust's the viewer's
position.
*/
{ // System.out.println("adjustHeight: " + newHeight);
double heightChg = newHeight - currLandHeight - (MOVE_STEP*zOffset);
Vector3d upVec = new Vector3d(0, heightChg, 0);
currLandHeight = newHeight; // update current height
zOffset = 0; // back on floor, so no offset
doMove(upVec);
} // end of adjustHeight()
private Vector3d tryMove(Vector3d theMove)
/* Calculate the effect of the given translation to get the
new (x,?, z) coord. Do not update the viewpoint's TG yet
*/
{ targetTG.getTransform( t3d );
toMove.setTranslation(theMove);
t3d.mul(toMove);
t3d.get( trans );
return trans;
} // end of tryMove()
synchronized private void doMove(Vector3d theMove)
/* Move the viewpoint by theMove offset.
This method can be called by KeyBehavior from moveBy()
and HeightFinder from adjustHeight(), so is synchronized.
*/
{
targetTG.getTransform(t3d);
toMove.setTranslation(theMove);
t3d.mul(toMove);
targetTG.setTransform(t3d); // update viewpoint's TG
} // end of doMove()
// -------------- rotation --------------------
private void rotateY(double radians)
// rotate about y-axis by radians
{
targetTG.getTransform(t3d);
toRot.rotY(radians);
t3d.mul(toRot);
targetTG.setTransform(t3d);
} // end of rotateY()
// --------------- current location ---------------------
private void printLandLocation()
// print the user's position in land coordinates
{
targetTG.getTransform(t3d);
t3d.get(trans);
trans.y -= MOVE_STEP*zOffset; // ignore user floating
Vector3d whereVec = land.worldToLand(trans);
System.out.println("Land location: (" + df.format(whereVec.x) + ", " +
df.format(whereVec.y) + ", " + df.format(whereVec.z) + ")");
} // end of printLandLocation()
} // end of KeyBehavior class
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -