How can I rotate an AR object in place? - java

I'm try the rotate the AR object in place use quaternion.axisAngle, but it's not working.
I just want to make a object rotate in place, like this.
This is my code.
//Right Rotation
ImageButton.OnTouchListener controll_BtnRigntRoation = new ImageButton.OnTouchListener() {
#Override
public boolean onTouch(View controllRightDown, MotionEvent event) {
if (controllRightDown == mRightRoation) {
ma.initModel();
for (int i=0; i<ma.infoArray.size(); i++) {
if (ma.infoArray.get(i).getNode().isSelected()) {
int finalI = i;
ma.arFragment.getArSceneView().getScene().addOnUpdateListener(frameTime -> {
Quaternion r1 = Quaternion.axisAngle(new Vector3(0, 0, 1), frameTime.getStartSeconds()* 180); //y 방향으로는 안됨..
ma.infoArray.get(finalI).getNode().setLocalRotation(r1);
});
}
}
}
return false;
}
};
Please help me.

You most likely want to use a transformable node - this will allow you do the type of rotation I think you are looking for:
https://developers.google.com/sceneform/reference/com/google/ar/sceneform/ux/TransformableNode
Example of use:
var newAnchorNode:AnchorNode = AnchorNode(newAnchor)
anchorNode.setParent(arFragment.getArSceneView().getScene());
var transNode = TransformableNode(arFragment.transformationSystem)
//Rotate the node - i.e set the direction it is 'looking'
transNode.setLookDirection(Vector3(0f, xPoint, yPoint), topDirection)
//Set it's parent to the anchor you created and set
//the renderable
transNode.setParent(anchorNode);
transNode.setRenderable(renderable);

Related

What is wrong with this custom "path finding" algo?

(I hope this is not a duplicate as the many questions I came into do not fit my need)
I'm developping a 2D grid based game with 2 players with grid. There are two players: blue and red, each one places a stone in cells. So I want to find a path passing throught all cells with the same color back to the starting point, BUT ONLY if there is at least ONE cell that contains opponent's stone.
From the screenshot above: The red stones here in the upper right do not form a valid path. And those in the center are not forming a path neither even though that should be one.
I'm able to find a path but it is somehow broken, it doesn't work as expected.
EDIT:
Pather class
public class Pather {
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT(0,1,-1),
RIGHT(1,1,0),
DOWN_RIGHT(2,1,1),
DOWN(3,0,1),
DOWN_LEFT(4,-1,1),
LEFT(5,-1,0),
UP_LEFT(6,-1,-1),
UP(7,0,-1);
public int index, x, y;
Neighbor(int index, int x, int y){
this.index = index;
this.x = x;
this.y = y;
}
}
private static Neighbor[] neighbors = Neighbor.values();
public static ArrayList<Path> findPaths(Stone[][] gameBoard){
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Point> checkedPoints = new ArrayList<>();
for (int i = 0; i < gameBoard.length ; i++) {
for (int j = 0; j < gameBoard[0].length; j++) {
if(gameBoard[i][j] != null){
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (i,j);
if(!checkedPoints.contains(origin)) {
potentialPath.add(origin);
checkedPoints.add(origin);
potentialPath = findPath(gameBoard, i, j, potentialPath, gameBoard[i][j].getPaint(), checkedPoints, Neighbor.RIGHT.index); //Changed from Neighbor.DOWN.index
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[i][j].getPaint()));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(Stone[][] gameBoard, int x, int y, ArrayList<Point> path, Paint color, ArrayList<Point> checkedPoints, int cameFrom){
int startClockwiseScanAtDirection = cameFrom + 5;
for (int i = startClockwiseScanAtDirection; i < startClockwiseScanAtDirection + 7; i++) {
// avoid ArrayIndexOutOfBounds
if(x+neighbors[i%8].x < 0 || y+neighbors[i%8].y < 0 || x+neighbors[i%8].x >= gameBoard.length || y+neighbors[i%8].y >= gameBoard[0].length)
continue;
// check if there's a stone that matches the current stone, we're scanning around
if(gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y] != null && gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y].getPaint() == color){
// found one
Point nextStone = new Point(x+neighbors[i%8].x,y+neighbors[i%8].y);
// is the point we just found the origin of the path?
if(nextStone.equals(path.get(0)) && path.size() > MIN_PATH_LENGTH) { //This seems to prevent drawing a path when we have less stone to form a path with
path.add(nextStone);
checkedPoints.add(nextStone);
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard,x+neighbors[i%8].x, y+neighbors[i%8].y, path, color, checkedPoints, i%8);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
Path class
public class Path {
public Paint getColor() {
return color;
}
public void setColor(Paint color) {
this.color = color;
}
public ArrayList<Point> getCoordinateList() {
return coordinateList;
}
public void setCoordinateList(ArrayList<Point> coordinateList) {
this.coordinateList = coordinateList;
}
private ArrayList<Point> coordinateList;
private Paint color;
public Path(ArrayList<Point> coordinatePath, Paint color){
this.coordinateList = coordinatePath;
this.color = color;
}
#Override
public String toString() {
return coordinateList.toString();
}
}
Here some case test:
Called in the MainActivity's onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gameGrid = findViewById(R.id.gameGrid);
bluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bluePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bluePaint.setColor(Color.BLUE);
redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setStyle(Paint.Style.FILL);
redPaint.setColor(Color.RED);
bgrBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrBluePaint.setStyle(Paint.Style.STROKE);
bgrBluePaint.setStrokeWidth(bgrStrokeWdth);
bgrBluePaint.setColor(Color.BLUE);
bgrRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrRedPaint.setStyle(Paint.Style.STROKE);
bgrRedPaint.setStrokeWidth(bgrStrokeWdth);
bgrRedPaint.setColor(Color.RED);
bluePlayer = new Stone(1,bluePaint, bgrBluePaint);
redPlayer = new Stone(2, redPaint, bgrRedPaint);
gameBoard = new Stone[100][100];
gameBoard[47][47]= redPlayer;
gameBoard[46][47]= bluePlayer;
gameBoard[44][48]= redPlayer; //REDs form a path when you place this stone in the last positioon
gameBoard[44][49]= redPlayer;
gameBoard[45][47]= redPlayer;
gameBoard[45][48]= bluePlayer;
gameBoard[45][49]= bluePlayer;
gameBoard[45][50]= redPlayer;
gameBoard[46][50]= bluePlayer;
gameBoard[46][49]= redPlayer;
gameBoard[46][48]= redPlayer;
gameBoard[47][50]= bluePlayer;
gameBoard[47][48]= bluePlayer;
gameBoard[47][49]= redPlayer;
gameBoard[48][50]= redPlayer;
gameBoard[48][49]= redPlayer;
gameBoard[48][48]= redPlayer;
gameBoard[49][50]= bluePlayer;
gameBoard[48][51]= redPlayer;
gameBoard[44][50] = bluePlayer;
ArrayList<Path> paths = Pather.findPaths(gameBoard);
gameGrid.setPaths(paths);
gameGrid.setGameBoard(gameBoard);
}
Placing stones at the following positions clears the path:
//Adding the following deletes the path
gameBoard[43][50] = redPlayer; //Adding this one in last position clears the path
gameBoard[45][51] = redPlayer;
I need to figure out how to make a condition that check for an opponent nearby first then validate the path.
EDIT 2:
Stone.java
public class Stone{
private int _player;
private Paint _paint, _bgrPaint;
public Stone(int player, Paint paint, Paint bgrPaint){
_player = player;
_paint = paint;
_bgrPaint = bgrPaint;
}
public int getPlayer() {
return _player;
}
public Paint getPaint() {
return _paint;
}
public Paint get_bgrPaint() {
return _bgrPaint;
}
}
Point.java
public class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object point) {
return this.x == ((Point) point).x && this.y == ((Point) point).y;
}
#Override
public String toString() {
return "("+x+","+y+")";
}
}
Screenshoot of what a valid path should look
A more-or-less standard way to approach this kind of problem is a "sweep line" algorithm. For simplicity, say we're looking for blue paths wrapping red points.
(You can process red paths wrapping blue points at the same time or in a second pass, but you can work that out later.)
You can search for "sweep line algorithm" to see how they work in related applications. The Wikipedia page isn't bad.
For this problem, the sweep line is a set of y-intervals. It's initialized using the leftmost (least x) blue point(s). It gets one interval for each vertically adjacent set of blue points. Each interval represents a vertical slice through a potential blue polygon.
The rest of the algorithm is to design the rules needed to update the scan line when it is moved one position to the right, incrementing x. This will be a matter of updating each interval. When a step finds a disconnected set of vertically adjacent points, a new interval is added. In some cases, intervals will "die out" because the potential polygon boundary dead-ends (think of a C shape). In other cases, they will "merge" because, at the corresponding x-coordinate, there is a set of 1 or more vertically adjacent connecting points. In still other cases, the polygon will complete successfully with a final set of 1 or more vertically adjacent points.
The details will be fiddly, but not hard to work out by case analysis.
To trace successful polygons, intervals can include two chains of preceding points: the upper and lower polygon boundaries.
The last consideration is whether a successfully closed polygon encloses at least one red point. But this is easy. If for any x-coordinate, the interval representing a polygon bracketed a red point, then the answer is yes. This can be recorded as an initially false boolean maintained in the interval, which is set true every time such a red point is seen. When a polygon is successfully closed, check the flag to see whether it should be used or not.
All the above can be made efficient for very large grids by using suitable data structures: interval trees for example. But if the grid is comparatively small, it should be fine to use simple lists. At any rate, consider prototyping it with a list for the sweep line first first and optimize with more complicated data structures later if needed.
As I wrote in my comments, without mvce it is very hard to offer detailed help.
From what I see in the code I figure you are trying to map all cyclic single-color paths on the board.
I made some documented changes in the code, hoping (without being able to properly check it) that it may help you improve your code.
Note that as Stone class was not posted, I changed the representation of the board to int[][]
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Phather {
private static final int RED = 2, BLUE = 1;
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT ( 1,-1),
RIGHT ( 1, 0),
DOWN_RIGHT( 1, 1),
DOWN ( 0, 1),
DOWN_LEFT (-1, 1),
LEFT (-1, 0),
UP_LEFT (-1,-1),
UP ( 0,-1);
int x, y;
Neighbor(int x, int y){
this.x = x;
this.y = y;
}
}
public static Set<Path> findPaths(int[][] gameBoard){
//use set to prevent duplicate paths
Set<Path> paths = new HashSet<>();
for (int x = 0; x < gameBoard.length ; x++) {
for (int y = 0; y < gameBoard[0].length; y++) {
//note that array indexes are [y][x] while point arguments are x,y
if(gameBoard[y][x] != 0){
//use set to prevent duplicate elements. initialize it to allow for
//overlapping paths (paths that contain some shared points)
Set<Point> checkedPoints = new HashSet<>();
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (x,y);
if(checkedPoints.add(origin)) { //add returns false if duplicate
potentialPath.add(origin);
potentialPath = findPath(gameBoard, x, y, potentialPath, checkedPoints);
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[y][x]));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(int[][] gameBoard, int x, int y,
ArrayList<Point> path, Set<Point> checkedPoints){
int color = gameBoard[y][x]; //no need for color as argument. get from stone
for(Neighbor neighbor : Neighbor.values()) {
int neighborX = x + neighbor.x, neighborY = y + neighbor.y;
// avoid ArrayIndexOutOfBounds
//todo: refactor to method isValidAddress(x,y,maxX, maxY)
if((neighborX < 0) || ( neighborY < 0) || (neighborY >= gameBoard.length)
|| (neighborX >= gameBoard[0].length)) {
continue;
}
// check if there's a stone that matches the current stone, we're scanning around
if((gameBoard[neighborY][neighborX] != 0) && (gameBoard[neighborY][neighborX] == color)){
// found one
Point nextStone = new Point(neighborX,neighborY);
// is the point we just found the origin of the path ?
if(nextStone.equals(path.get(0)) && (path.size() > MIN_PATH_LENGTH)) {
path.add(nextStone); //do you want it in path twice ?
//checkedPoints.add(nextStone); //if added to path before, it is already in checkedPoints
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard, neighborX, neighborY, path, checkedPoints);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
class Path {
private ArrayList<Point> coordinateList;
private int color;
Path(ArrayList<Point> coordinatePath, int color){
coordinateList = coordinatePath;
this.color = color;
}
int getColor() { return color; }
#Override
public String toString() {
return coordinateList.toString();
}
List<Point> getPoints() { return coordinateList; }
int size() { return coordinateList.size(); }
#Override
public boolean equals(Object p){
if (p == this) { return true; }
if (p == null) { return false;}
if (!(p instanceof Path)) {return false; }
Path path = (Path)p;
return getPoints().containsAll(path.getPoints())
&& path.getPoints().containsAll(getPoints());
}
}

Java to C# conversion. How do i draw a rectangle on my bitmap?

Firstly, i am a complete noob at both C# and Java.
So i have been given this assignment to convert a java applet into C#, i have managed to do everything apart from drawing a rectangle on the screen via drag and drop using mouse events.
Whats supposed to happen is when i click and drag my mouse across the screen a rectangle with no fill and white border should appear. The code i have below is just a white screen with a red cross through it, if i comment out the if(action) statement in the form1_Paint then it works but no rectangle so it must be that code that messing it up.
http://gyazo.com/b2506b8c2ea9b304e34172c42ce98aab <-- what it should look like
http://gyazo.com/a8764ac9f5380f0109623d7a7750ddb6 <-- what it actually looks like
[update]
I have now got a rectangle do display but it happens on the MouseUp event rather than creating it as i am dragging my mouse. The obvious next step was to move it to a different mouse event like mouseMove but then it really messes up and created rectangles constantly as i make it bigger. How can i make it constantly resize the rectangle as i drag my mouse and not keep creating rectangles constantly?
The code
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
}
//added load method
private void Form1_Load(object sender, EventArgs e)//runs functions on load
{
init();
start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (action)
{
xe = e.X;
ye = e.Y;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
xs = xe = e.X;
ys = ye = e.Y; // starting point y
Form1_MouseMove(sender, e);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
Pen pen = new Pen(Color.White);
g.DrawRectangle(pen, xs, ys, Math.Abs(xs - xe), Math.Abs(ys - ye));
}
int z, w;
//e.consume();
//xe = e.X;
//ye = e.Y;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
this.Invalidate();
//Repaint();
}
The biggest problem in your code is this statement in the Form1_Paint() method:
g1.Dispose();
You should never be disposing the Graphics instance passed to you. It belongs to the framework, not your code. But you should especially never dispose an object that you plan to use later. When you dispose it here, then the Graphics instance isn't valid later on when you try to draw the rectangle.
Note that this is the same as in Java. I hope the original Java code didn't call Graphics.dispose() too!
Some other suggestions:
when creating a new Pen object, add a using statement to ensure the Pen instance you create is disposed properly (you do own that one! :) ). In this case though, you don't need to create a new Pen object...just use the stock Pen provided by .NET. I.e. Pens.White.
you don't appear to be calling Invalidate() in the MouseDown and MouseMove event handlers. You won't get any visual feedback unless you do that, because the Paint event handler won't be called.
Fix the code so it looks like this:
// Little helper method :)
private static void Swap<T>(ref T t1, ref T t2)
{
T temp = t1;
t1 = t2;
t2 = t1;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
if (action)
{
//g.setColor(Color.White);
if (xe < xs)
{
Swap(ref xs, ref xe);
}
if (ye < ys)
{
Swap(ref ys, ref ye);
}
g1.DrawRectangle(Pens.White, xs, ys, (xe - xs), (ye - ys));
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
// e.consume();
if (action)
{
xe = e.X;
ye = e.Y;
Invalidate();
//repaint();
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
if (action)
{
xs = xe = e.X;
ys = ye = e.Y;
Invalidate();
}
}
I've had a look here, this doesn't seem to fix my problem, the Invalidate();'s make it stutter and when I have it in Form1_Paint it doesn't draw correctly, either draws before straight onto the form, straight after I've zoomed but doesn't actually appear when I'm dragging in my zoom!

How to fix the movement of 3D object?

I got a 3D spaceship that moves forward and backward in the 3D scene correctly but the rightward and leftward movements are not right and what the A and D buttons do seem to vary with the camera position. This is the listener code which works for buttons W (forward) and S (backward) while buttons A and D don't do exactly what they should.
When I start the 3D space scene the steering of the spaceship is working and the buttons A and D move the spaceship left and right but after a changed the camera and rotated the view, the buttons A and D are still opposite directions but not to the left and right but depend on the camera's position.
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
Can you help me and tell me what should be done to fix the right and left movements? The entire code is
public class SpaceStation extends SimpleApplication implements AnalogListener,
ActionListener {
private PlanetAppState planetAppState;
private Geometry mark;
private Node ufoNode;
private Node spaceStationNode;
private Node jumpgateNode;
private Node jumpgateNode2;
private BetterCharacterControl ufoControl;
CameraNode camNode;
boolean rotate = false;
Vector3f direction = new Vector3f();
private BulletAppState bulletAppState;
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(1024, 768);
SpaceStation app = new SpaceStation();
app.setSettings(settings);
// app.showSettings = true;
app.start();
}
#Override
public void simpleInitApp() {
// Only show severe errors in log
java.util.logging.Logger.getLogger("com.jme3").setLevel(
java.util.logging.Level.SEVERE);
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
bulletAppState.setDebugEnabled(false);
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-.1f, 0f, -1f));
sun.setColor(new ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f));
rootNode.addLight(sun);
// Add sky
Node sceneNode = new Node("Scene");
sceneNode.attachChild(Utility.createSkyBox(this.getAssetManager(),
"Textures/blue-glow-1024.dds"));
rootNode.attachChild(sceneNode);
// Create collision test mark
Sphere sphere = new Sphere(30, 30, 5f);
mark = new Geometry("mark", sphere);
Material mark_mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mark_mat.setColor("Color", ColorRGBA.Red);
mark.setMaterial(mark_mat);
// Add planet app state
planetAppState = new PlanetAppState(rootNode, sun);
stateManager.attach(planetAppState);
// Add planet
FractalDataSource planetDataSource = new FractalDataSource(4);
planetDataSource.setHeightScale(900f);
Planet planet = Utility.createEarthLikePlanet(getAssetManager(),
293710.0f, null, planetDataSource);
planetAppState.addPlanet(planet);
rootNode.attachChild(planet);
// Add moon
FractalDataSource moonDataSource = new FractalDataSource(5);
moonDataSource.setHeightScale(300f);
Planet moon = Utility.createMoonLikePlanet(getAssetManager(), 50000,
moonDataSource);
planetAppState.addPlanet(moon);
rootNode.attachChild(moon);
moon.setLocalTranslation(new Vector3f(10f, 10f, 505000f));//-950000f, 0f, 0f);
// add saucer
ufoNode = (Node) assetManager.loadModel("usaucer_v01.j3o");
ufoNode.setLocalScale(100f);
ufoNode.setLocalTranslation((new Vector3f(1000f, -1000f, 328000f)));
jumpgateNode = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode.setLocalScale(10000f);
jumpgateNode.setLocalTranslation((new Vector3f(10f, 10f, 708000f)));
spaceStationNode = (Node) assetManager.loadModel("SpaceStation.j3o");
spaceStationNode.setLocalScale(4000f);
spaceStationNode.setLocalTranslation((new Vector3f(10000f, -10f, 425000f)));
jumpgateNode2 = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode2.setLocalScale(10000f);
jumpgateNode2.setLocalTranslation((new Vector3f(10f, 10f, 798300f)));
/* This quaternion stores a 180 degree rolling rotation */
// Quaternion roll180 = new Quaternion();
// roll180.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1));
/* The rotation is applied: The object rolls by 180 degrees. */
// ufoNode.setLocalRotation(roll180);
rootNode.attachChild(jumpgateNode);
rootNode.attachChild(jumpgateNode2);
rootNode.attachChild(spaceStationNode);
// creating the camera Node
camNode = new CameraNode("CamNode", cam);
// Setting the direction to Spatial to camera, this means the camera
// will copy the movements of the Node
camNode.setControlDir(ControlDirection.SpatialToCamera);
// attaching the camNode to the teaNode
ufoNode.attachChild(camNode);
// setting the local translation of the cam node to move it away a bit
camNode.setLocalTranslation(new Vector3f(-40, 0, 0));
// setting the camNode to look at the teaNode
camNode.lookAt(ufoNode.getLocalTranslation(), Vector3f.UNIT_Y);
// disable the default 1st-person flyCam (don't forget this!!)
ufoControl = new BetterCharacterControl(100000f, 80000f, 5000f);// (2, 4, 0.5f);
// radius (meters), height (meters), gravity (mass)
//ufoNode.addControl(ufoControl);
//rootNode.attachChild(ninjaNode);
//bulletAppState.getPhysicsSpace().add(ufoControl);
//getPhysicsSpace().add(ufoControl);
rootNode.attachChild(ufoNode);
flyCam.setEnabled(false);
registerInput();
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
public void registerInput() {
inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP),
new KeyTrigger(keyInput.KEY_W));
inputManager.addMapping("moveBackward", new KeyTrigger(
keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
inputManager.addMapping("moveRight",
new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(
keyInput.KEY_D));
inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT),
new KeyTrigger(keyInput.KEY_A));
inputManager.addMapping("toggleRotate", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT));
inputManager.addMapping("rotateRight", new MouseAxisTrigger(
MouseInput.AXIS_X, true));
inputManager.addMapping("rotateLeft", new MouseAxisTrigger(
MouseInput.AXIS_X, false));
inputManager.addMapping("rotateUp", new MouseAxisTrigger(
MouseInput.AXIS_Y, true));
inputManager.addMapping("rotateDown", new MouseAxisTrigger(
MouseInput.AXIS_Y, false));
inputManager.addListener(this, "moveForward", "moveBackward",
"moveRight", "moveLeft");
inputManager.addListener(this, "rotateRight", "rotateLeft", "rotateUp",
"rotateDown", "toggleRotate");
// Toggle mouse cursor
inputManager.addMapping("TOGGLE_CURSOR", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT), new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, "TOGGLE_CURSOR");
// Toggle wireframe
inputManager.addMapping("TOGGLE_WIREFRAME", new KeyTrigger(
KeyInput.KEY_T));
inputManager.addListener(actionListener, "TOGGLE_WIREFRAME");
// Collision test
inputManager.addMapping("COLLISION_TEST", new MouseButtonTrigger(
MouseInput.BUTTON_RIGHT));
inputManager.addListener(actionListener, "COLLISION_TEST");
}
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
public void onAction(String name, boolean keyPressed, float tpf) {
// toggling rotation on or off
if (name.equals("toggleRotate") && keyPressed) {
rotate = true;
inputManager.setCursorVisible(false);
}
if (name.equals("toggleRotate") && !keyPressed) {
rotate = false;
inputManager.setCursorVisible(true);
}
if (name.equals("TOGGLE_CURSOR") && !keyPressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !keyPressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !keyPressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size() + "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry().getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean pressed, float tpf) {
if (name.equals("TOGGLE_CURSOR") && !pressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !pressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !pressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size()
+ "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry()
.getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
};
}
Going off your last comment, I am posting this as an answer (although I'm not exactly sure what to use as a cross vector.
When retrieving the cross vector, we are looking to get a perpendicular to the straight line out the front of the craft, and the vertical line that is perpendicular to the straight line, going vertically through the center of the craft.
I assume that direction is our forward-direction vector, in which case (regardless of view) we want to cross this with the vertical line that goes through the center of the craft. The crossLocal of these two vectors would be a perpendicular line to both, either going out of the left or right of the craft (regardless of camera or craft orientation).
for my code fix, I will assume craftSkewer is an imaginary skewer that runs through the center of the craft, vertically.
direction.crossLocal(craftSkewer.UNIT_Y).multLocal(movement * tpf);
I think the reason this works initially is due to the UNIT_Y returning 0 - But after moving craft or camera, it is recalculated incorrectly?

Collision detection java giving unexpected results

My code for collision detection works but as soon as I set my players & monsters velocity to more than 1 I get weird results how can I solve this?
with velocity of 1
with velocity of 4
My collision detection:
static boolean collisionDown(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX(), e.getY() + e.getVelocity(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size );
if (player.intersects(block))
{
//e.goUp(1);
return true;
}
}
return false;
}
static boolean collisionUp(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX(), e.getY() - e.getVelocity(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
static boolean collisionRight(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX() + e.getVelocity(), e.getY(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
static boolean collisionLeft(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX() - e.getVelocity(), e.getY(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
Where i use the collision detection
void goUp(int v)
{
if(!Block.collisionUp(this))
y -= v;
}
void goDown(int v)
{
if(!Block.collisionDown(this))
y += v;
}
void goRight(int v)
{
if(!Block.collisionRight(this))
x += v;
}
void goLeft(int v)
{
if(!Block.collisionLeft(this))
x -= v;
}
Thank you
Your collision methods look just fine to me, assuming you are evaluating where the character will be at the next iteration of the game. I'm willing to bet though, that when there will be a collision, you move your character at an offset to move it out of the collision box and back into a valid area to compensate. When the velocity is higher, it is magnifying this little side effect. Double check all the places that call these methods and follow the logic that follows when these methods return true (principally those in the Y direction).
An additional thing to try, increase the velocity of the character to something even higher, or something in between. If your character moves farther way and closer as you manipulate the value, this is very likely what is happening.
On a side note, note this line in all of your methods
Rectangle player = new Rectangle(e.getX(), e.getY() - e.getVelocity(), e.getWidth(), e.getHeight());
This rectangle will not change (assuming you have not implemented threading into your game) during the duration of the method call, so recreating it every iteration of the for loop is just taking up time. Consider modifying the methods to this:
static boolean collisionDown(Entity e)
{
Rectangle player = new Rectangle(e.getX(), e.getY() + e.getVelocity(), e.getWidth(), e.getHeight());
for(Block i : Game.blocks)
{
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size );
if (player.intersects(block))
{
//e.goUp(1);
return true;
}
}
return false;
}
What I did for collision detection is develop a class called CollissionDetector. It has some logic within it for such methods as the following:
public boolean isColliding();
public boolean collided(Rectangular r1, Rectangular r2);
Rectangular is an interface with one method:
public Rectangle getRectangle();
So my CollisionDetector class worked with anything that could implement the interface called Rectangular and return a Rectangle.
The problem with some code that is supposed to detect collissions is that it reports the same collision more than once.
With my collision detector, the collission detector object would keep track of when the collision starting, whether the two objects were still colliding, and when the collision ended.
It seems like I also had a CollissionListener. I know that this is all very complex, but it did work. The CollisionListener had methods such as:
public void CollissionStarted();
public void CollissionEnded();
I wish that I could give you an SSCCE: http://sscce.org but I don't have the code anymore.

How should I alter my collision detection for a rotating line?

I create a rectangle for my "line", it's a rotating 'laser sight'
public Rectangle getLaserBox() {
float lOriginX = (leon.getPosition().x + 0.27f);
float lOriginY = (leon.getPosition().y + 0.7f);
float lEndX = lOriginX + (float)Math.cos((leonAimLaserSprite.getRotation())/57) * 5f;
float lEndY = lOriginY + (float)Math.sin((leonAimLaserSprite.getRotation())/57) * 5f;
Rectangle laserBox = new Rectangle(lOriginX, lOriginY, lEndX, lEndY);
return laserBox;
}
Then I have a method that checks for overlap of the rectangles and is supposed to shorten the 'laser sight' sprite if overlap is detected. I call the laserCol() method in my render method for now (I know I'm breaking MVC, just trying to get it working), and my laserWidth is applied as the width of the laser sprite.
private float laserWidth;
public void laserCol() {
Vector2 laserOrigin = new Vector2(leon.getPosition().x + 0.55f, leon.getPosition().y + 0.7f);
boolean laserIsCol = false;
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
laserWidth = laserOrigin.dst(t.getPosition().x + 0.1f, t.getPosition().y + 0.7f);
if (Intersector.overlapRectangles(getLaserBox(), t.getBounds())) {
laserIsCol = true;
}
}
if (laserIsCol) {
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
laserWidth = laserOrigin.dst(t.getPosition().x, t.getPosition().y + t.getBounds().y);
}
}
if (!laserIsCol) {
laserWidth = 8f;
}
}
But as you can see in the screenshot, the laser does not shorten. I've looked at other examples but can't seem to understand a better way to do this.
So after adding the single object which I called thing in my code, I decided to check my sprite boundingbox I created and it looked like this.
After that, I changed some code, and am trying to use a ray, I've gotten it to work somewhat but it's not as close as I'd like, any suggestions?
public Ray getLaserRay() {
lOriginX = (leon.getPosition().x + 0.27f);
lOriginY = (leon.getPosition().y + 0.7f);
lEndX = lOriginX + (float)Math.cos((leonAimLaserSprite.getRotation())/57) * 10f;
lEndY = lOriginY + (float)Math.sin((leonAimLaserSprite.getRotation())/57) * 10f;
laserO = new Vector3(lOriginX, lOriginY, 0);
laserD = new Vector3(lEndX, lEndY, 0);
Ray laserRay = new Ray(laserO, laserD);
return laserRay;
}
private float laserWidth;
public void laserCol() {
Vector2 laserOrigin = new Vector2(leon.getPosition().x + 0.55f, leon.getPosition().y + 0.7f);
boolean laserIsCol = false;
if (Intersector.intersectRayBoundsFast(getLaserRay(), thing.getBB())) {
laserIsCol = true;
}
if (laserIsCol) {
laserWidth = laserOrigin.dst(thing.getPosition().x, thing.getPosition().y);
}
if (!laserIsCol) {
laserWidth = 10f;
}
}
I ended up using 2 line segments to get the collision detection to work. I made one for the laser sight, and one for my object(enemy), that extended from the lower x point to the top x point. Basically the same code except I used the segment intersector from libgdx.
Anyone have an idea of how to make a bullet or damage happen at laser sight spot?
Without having more information I just can guess, but I assume the problem might be that you're calculating the width using all the tiles in your list/array. Instead, you should just use the tile the laser collides with to calculate the width.
Something like this:
laserWidth = 8f; //default if no collision is found
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
if (Intersector.overlapRectangles(getLaserBox(), t.getBounds())) {
laserWidth = laserOrigin.dst(t.getPosition().x, t.getPosition().y + t.getBounds().y);
break;
}
}
Please note that I just reused your code, I don't know if this is correct or if there are some wrong calculations in it. As I said in my comment, you should debug the calculations and look where they start to get wrong.

Categories