Watch this image example:
Hi guys, I have a question about "Intersector", what I want is when I click the screen, I get the intersection, then I put a 3d modals on the ground according the intersection point. The problem was I can't get the intersection.
Here is my code
public Vector3 getIntersect(int screenX, int screenY) {
Ray ray = cam.getPickRay(screenX, screenY);
Vector3 ins = new Vector3();
for (int i = 0; i < parentModels.size; ++i) {
final GameObject go = parentModels.get(i);
Intersector.intersectRayBounds(ray, go.bounds, ins);
}
return ins;
}
GameObject extends form ModelInstance.
Related
Using libgdx, I want to discard occluded sprites using a depth buffer. To do so I use the provided Decal and DecalBatch with an OrthographicCamera and I set the z position manually.
Depending my sprite position on the x and y axes, the depth buffer works or not as expected.
red square z = 98
green square z = 10
The square are 50% transparent so I can see if the depth test occurred as expected.
Here the test code:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.decals.CameraGroupStrategy;
import com.badlogic.gdx.graphics.g3d.decals.Decal;
import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import fr.t4c.ui.GdxTest;
public class DecalTest extends GdxTest {
DecalBatch batch;
Array<Decal> decals = new Array<Decal>();
OrthographicCamera camera;
OrthoCamController controller;
FPSLogger logger = new FPSLogger();
Decal redDecal;
Decal greenDecal;
public void create() {
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
//camera.near = 1;
camera.position.set(600, 600, 100);
camera.near = 1;
camera.far = 100;
controller = new OrthoCamController(camera);
Gdx.input.setInputProcessor(controller);
batch = new DecalBatch(new CameraGroupStrategy(camera));
TextureRegion[] textures = {
new TextureRegion(new Texture(Gdx.files.internal("src/test/resources/redsquare.png"))),
new TextureRegion(new Texture(Gdx.files.internal("src/test/resources/greensquare.png")
))};
redDecal = Decal.newDecal(textures[0], true);
redDecal.setPosition(600, 600, 98f);
decals.add(redDecal);
greenDecal = Decal.newDecal(textures[1], true);
greenDecal.setPosition(630, 632f, 10f);
decals.add(greenDecal);
Decal decal = Decal.newDecal(textures[0], true);
decal.setPosition(400, 500, 98f);
decals.add(decal);
decal = Decal.newDecal(textures[1], true);
decal.setPosition(430f, 532f, 10f);
decals.add(decal);
}
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glDepthFunc(GL20.GL_LEQUAL);
camera.update();
for (int i = 0; i < decals.size; i++) {
Decal decal = decals.get(i);
batch.add(decal);
}
batch.flush();
}
#Override
public void dispose() {
batch.dispose();
}
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.useGL30 = false;
cfg.width = 640;
cfg.height = 480;
cfg.resizable = false;
cfg.foregroundFPS = 0; // Setting to 0 disables foreground fps
// throttling
cfg.backgroundFPS = 0; // Setting to 0 disables background fps
new LwjglApplication(new DecalTest(), cfg);
}
}
It is a depth buffer precision problem, the orientation of the camera that messed up the calcul or something else?
Edit:
I expect the sprites being occluded if they are behind another. So in my example the red square should occlude the green part he is in front of.
The left bottom squares have the right behaviour but the upper right squares does not. The things is red squares have the same Z value and green squares have the same Z value too(different than the red squares Z of course). So the only things that made the square couples different is their x and y position, which should not impact the depth test.
So, what I want is a consistent depth test behaviour that occluded hided texture as we see whith the bottom left squares no matter their x and y position.
According to the comment, I added information about what I expect.
Decal and DecalBatch rely on GroupStrategy for depth sorting, NOT the camera. Additionally, these strategies sort depth EITHER by the distance from the camera OR by only the Z axis, which would required for a perspective camera i.e. a decal that is closer and should occlude as measured by Z, might be further as measured by distance from camera.
i.e. (x,y,z)
Camera 0,0,1.
Decal A 1,1,0 (Z distance 1, vector distance 1.73)
Decal B 0,0,-0.1 (Z distance 1.1, vector distance 1.1)
The depth strategy you chose for the above decals could either consider A or B first.
The most common recommended GroupStrategy is CameraGroupStrategy, but this does not sort by Z, but uses camera distance. If you intialise DecalBatch instead with SimpleOrthoGroupStrategy then depth will be sorted purely by Z, here is depth sort for it, you can look at the other group strategies and see its purely absolute distance.
class Comparator implements java.util.Comparator<Decal> {
#Override
public int compare (Decal a, Decal b) {
if (a.getZ() == b.getZ()) return 0;
return a.getZ() - b.getZ() < 0 ? -1 : 1;
}
}
first of all, i'm not a native english speaker but, still, i'll try my best to be understandable and as clear as possible.
So, in my programming class, I need to make a Tile based game (like zelda, for exemple) with animate cc (flash). On a map, I want to make a dance floor with tiles that changes on the rhythm of a music. these tiles are movieclip with two frame, one white and one red.
This is how the tiles are generated:
private function createGrid(): void {
grid = new MovieClip();
addChild(grid);
for (var r: int = 0; r < nbRow; r++) {
for (var c: int = 0; c < nbCol; c++) {
var t: Tiles = new Tiles();
t.x = t.width * c;
t.y = t.height * r;
grid.addChild(t);
}
}
grid.x = 15; //center the grid on x
grid.y = 35; //center the grid on y
}
This is the Tiles Class :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Tiles extends MovieClip {
private var rand:int;
public function Tiles() {
// constructor code
getTiles();
}
public function getTiles():void {
random();
setColor();
}
private function random() : void{
rand = Math.floor(Math.random()*100)+1;
}
private function setColor() : void{
if(rand<=30){
gotoAndStop(8); //red frame
}else{
gotoAndStop(7); //white frame
}
}
}
}
createGrid() place the tiles as soon as the map is placed on the stage and stock every tiles in the MovieClip grid. Now, I want the tiles to change randomly between red and white on the beat of a streamed music (and keep the ratio of 30% red tiles and 70% white tiles)
var s: Sound = new Sound();
var sc: SoundChannel;
s.load(new URLRequest("GameSong_mixdown.mp3"));
sc = s.play(0, 1000);
I know i need the leftpeek properties of my soundchannel to achieve that but,for now, I do my test with a button that trigger this function:
private function setTiles(e: Event): void {
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++) {
grid.getChildAt(i).getTiles();
}
}
Right now, the problem is : I'm unable to acces my Tiles method. I did a trace on grid,getChildAt(i), and saw all instances of my tiles in the console. So, i know for sure that every instances of my tiles are stored in grid. But, I don't know why, grid.getChildAt(i).getTiles(); doesn't work (and every other method from Tiles). The error message is: Call to a possibly udefined method getTiles through a reference with static type flash.display:DisplayObject
Does someone know what i'm doing wrong ?
ps: I translated all my class name, var name, etc from french to
english to make the code clearer.
Your mistake is that getChildAt(...) method has a return type of DisplayObject which is neither dynamic (will not let you access random properties) nor it have DisplayObject.getTiles() method.
All you need is to tell the program that this object is actually of Tiles class:
private function setTiles(e:Event):void
{
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++)
{
// Cast display objects to Tiles class.
var aTiles:Tiles = grid.getChildAt(i) as Tiles;
// Call the method.
aTiles.getTiles();
}
}
I am creating a Table that contains multiple actors. The table also contains an Image which has a ClickListener attached. From other actors I draw an arrow when they are tapped or clicked, arrow that can target the Image from the Table. I want to know when the arrow enters and exits the Image from the Table. The problem is that the enter event doesn't always trigger, or it triggers only from a portion of the image or exit gets triggered as soon as enter was triggered.
Any idea what could be wrong?
oponentHand = new Table();
for (int i = 0; i < 4; i++) {
oponentHand.add().width(game.developingWidth / (11.5f)).height(game.developingHeight / (5.5f)).padLeft(5)
.expand();
}
Texture oponentTexture = new Texture(Gdx.files.internal("data/mihai.jpg"));
Table tempTable = new Table();
oponentPortrait = new Image(oponentTexture);
oponentLife = new Label("Life", textsStyle);
tempTable.add(oponentLife).row();
oponentPortrait.setTouchable(Touchable.enabled);
tempTable.add(oponentPortrait).expand().fill();
oponentPortrait.addListener(new ClickListener() {
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
super.enter(event, x, y, pointer, fromActor);
isOnTarget = true;
targetedCardType = "enemyHero";
System.out.println("Entering enemy hero");
}
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
super.exit(event, y, y, pointer, toActor);
if (pointer == -1) {
isOnTarget = false;
System.out.println("Exiting enemy hero");
}
}
});
Texture oponentPowerTexture1 = new Texture(Gdx.files.internal("data/powSword.png"));
Texture oponentPowerTexture2 = new Texture(Gdx.files.internal("data/powShield.png"));
Image firstOponentPower = new Image(oponentPowerTexture1);
Image secondOponentPower = new Image(oponentPowerTexture2);
oponentHand.add(firstOponentPower).width(game.developingWidth / (11.5f)).height(game.developingHeight / (6))
.padLeft(5);
oponentHand.add(tempTable).width(game.developingWidth / (11.5f)).height(game.developingHeight / (6)).fill().padLeft(5);
oponentHand.add(secondOponentPower).width(game.developingWidth / (11.5f)).height(game.developingHeight / (6))
.padLeft(5);
for (int i = 0; i < 4; i++) {
oponentHand.add().width(game.developingWidth / (11.5f)).height(game.developingHeight / (6)).padLeft(5)
.expand();
}
I found the problem that was causing the enter/exit events not to trigger. I was adding another actor to the stage later in the game and by coincidence it happened to be added on top of the image on which I was trying to verify the enter/exit events. I was fading in and then fading out the actor that I was adding to the stage by using Action so it was only visible for a very short amount of time. The problem was that I was never removing it from the stage, because fade out only changes the alpha of the actor, so it wasn't allowing the image below it to get the events. I solved it by removing it from the stage after fading it out using Actions.removeActor().
May be this will be a very basic question,but as a newbie,I am confused of it.
In my libGDX Project,I wants to move the player.
Initially player is on left side.For the first tap player should move to opposite side and occupy the position there.for the next tap it should move to left.and will continue like this.
I implemented this logic.Now problem is that my player is not at all moving but just occupying the opposite position on tap.I used a velocity value but it does not make any effect on the code.
I want to make it move along the way,not just occupying the position.
Please help.
public float ninjaX = Constants.W_WIDTH;
public float ninjaY =Constants.WORLD_HEIGHT/2+Constants.WORLD_HEIGHT/4;
public float ninjaVelocity =100f;
public boolean isLeftBool=true;
public void ninjaMove() {
if (isLeftBool) {
ninjaX = ninjaX+ ninjaVelocity;
setPosition(Constants.WORLD_WIDTH - (Constants.W_WIDTH+Constants.PLAYER_HEIGHT/2), ninjaY);
isLeftBool = false;
}
else
{
ninjaX=ninjaX-ninjaVelocity;
setPosition(ninjaX,ninjaY);
isLeftBool = true;
}
}
updating player in render:
if (MyInputProcessor.isTap) {
MyInputProcessor.isTap = false;
ninja.ninjaMove();
}
Your player coordinates changes should depend on delta time:
x1 = x0 + v * Δt
x1 - is new coordinate
x0 - is current coordinate
v - speed
Δt - time span between x0 and x1
LibGDX provides the method to get the time span between current and last frames:
Gdx.graphics.getDeltaTime();
So, changing your code accordingly should make your player move smoothly (depends on velocity):
ninjaX = ninjaX + ninjaVelocity * Gdx.graphics.getDeltaTime();
Same thing for substraction in your else block.
You also probably want to set x coordinate (ninjaX) as well as y (currently you're no passing it in your setPosition method):
setPosition(ninjaX, ninjaY);
*I'm guessing here because I don't know what the method does
This is just an example of how you could solve this.
You don't give an explanation of what setPosition() does and why it is called so I will ignore it.
In your render():
public void render(float delta){
....
ninja.update(delta);
}
In your Ninja.class
private final Vector2 leftPos = new Vector(20, 60);
private final Vector2 rightPos = new Vector(80, 60);
private Rectangle bound = new Rectangle(80,60,32,32);
private Vector2 currentPos;
private float moveSpeed = 100f;
public Ninja(){
currentPos = leftPos;
}
public void update(float delta){
if(bound.x > currentPos.x)bound.x += moveSpeed * delta;
else bound.x -= moveSpeed * delta;
}
public void move(){
if(currentPos == leftPos)currentPos = rightPos;
else currentPos = leftPos;
}
Then in you input processor call ninja.move() in touchDown()
None of this is tested.
The reason you need to multiply moveSpeed with delta time is to make movement independent of fps. This way ninja will move the same distance in the same time no matter how many fps the game runs.
I am making a Sim City like game. There are lots of tiles. When I first started. I was just using a tilesheet. I was copying the necessary pieaces from the tilesheet. on to a blank bitMapData. I then took the bitMapData and put it into a bitMap which I then put into a DisplayObject. It worked great!
tileSheet:BitMapData <----- data is already in
loop { loop through and tiled
bg:bitMapData= new bitMapData();
bg.copyPixel(tileSheet,rect,point);
}
canvas.BitMap(bg);
addChild(canvas);
Only problem was I needed to make my tiles interactive. I needed to highlight them and change colors and stuff. So I used the Sprite object. It works great but I can only have so many on the stage at once. or else it moves slow when I scroll. I need something Lighter then a sprite, but yet I can still turn into a object to make interactive. Anyone have any ideas ???
If you have a lot of tiles, that will impact performance because Flash needs to update the transformations of a lot of display objects (which internally means a lot of matrix calculations, and subsequent redraws of big areas of the screen.)
There is another way to achieve interactivity, if you find that you must use a single bitmap data for performance. Keep an "abstract" (i.e. not graphical) data model in memory, that stores your game state. Make sure that you are able to read from your store where a certain element is positioned in the game world. Then you can use a flat bitmap data to render the game world, because the individual positions are stored elsewhere.
When the user clicks the DisplayObject containing the bitmap data (a Sprite in which the bitmap is drawn using a bitmap fill, or that wraps a Bitmap), look in your model which of your game elements was hit by that click.
// myTileSprite is a Sprite with a bitmap fill
myTileSprite.addEventListener(MouseEvent.CLICK, handleWorldClick);
function handleWorldClick(ev : MouseEvent) : void
{
var i : int;
// Loop through all game element data models
for (i=0; i<myGameElements.length; i++) {
// Test the mouse position against the element model
if (myGameElements[i].hitTest(myTileSprite.mouseX, myTileSprite.mouseY)) {
trace('this was the element that was clicked: '+myGameElements[i].toString());
}
}
}
Here, whenever the player clicks the world graphics, the loop tries to find that element which was directly under the mouse position. You will need to implement a hitTest() method on all your game element data models, of course. Such a method simply checks the supplied world space position against the tile's area:
// GameElement.hitTest():
/**
* Tests a world position against the position and area of this game
* element tile. Returns a boolean indicating whether this tile was hit.
*/
public function hitTest(mouseX : Number, mouseY : Number) : void
{
var rect : Rectangle = new Rectangle(this.worldX, this.worldY, this.width, this.height);
if (mouseX > rect.left && mouseX < rect.right
&& mouseY > rect.top && mouseY < rect.top) {
return true;
}
else return false;
}
The GameElement class is not an display object, but has worldX and worldY properties indicating where it is located in the world. It's width and height properties define it's dimensions.
The trick from hereon is to make sure that the rendered bitmap and your model storage is synchronized, so that a tile's position on the bitmap really corresponds to it's worldX/worldY properties in the data model.
I am one step ahead of you. And that is a great idea. Its alot easier to keep a data representation of the world when the tiles are squared. I therefore can take my mouseX/tileWidth, and thats hw many columns I moved from left to right. same with the Y axis.
Not only that but coordinates start at top left corner.
But issue I have is that my tiles are Isometric. So instead of the X axis start off like...
012345678
0
1
2
3
4
5
6
7
8
My tiles are aligned like...
00
1 1
2 2
3 3
4 4
5 6
its a little sloppy. but the right side represents the y axis and the left represents the x axis. and the center origin is in the center of the screen. not on the top left. I am trying to figure out how to measure where my mouse is from the center and out on both sides. This sounds extremely difficult. I am not sure if its possible. The game is suppose to be like a sim city like game. The first sim city was squares not isometric. I dont think they went isometric until they started using 3d. I wonder if its possible to create a illusion of isometric on a square tile.
Ive been reading this great book on isometrics. They show to calculate tiles in 3d space. and even calculate your mouse in 3d space as well. here is the code. Its alot, but I hope someone else understands it more then I. The book was written by jobe makar on building multiplayer worlds. I wanted to share it because the code it is pretty simple as far as amount of code put into it. only 2 classes needed. I am not that good with trigonometry. so I cant really interpret how the math is getting the results. hopefully someone can explain that for me :D.
Y coordinates are not given because the width is = to height. The coordinates method is just a custom made Point class which holds x, y and z.
package com.gamebook.grid {
import com.gamebook.utils.geom.Coordinate;
import com.gamebook.utils.Isometric;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/**
* ...
* #author Jobe Makar - jobe#electrotank.com
*/
public class Map extends MovieClip{
private var _grid:Array;
private var _iso:Isometric;
private var _tileWidthOnScreen:int;
private var _tileHeightOnScreen:int;
private var _tileWidth:Number;
private var _tileHeight:Number;
private var _cols:int;
private var _rows:int;
private var _lastTile:Tile;
public function Map() {
initialize();
}
private function initialize():void{
_iso = new Isometric();
//when mapped to the screen the tile makes a diamond of these dimensions
_tileWidthOnScreen = 64;
_tileHeightOnScreen = 32;
//figure out the width of the tile in 3D space
_tileWidth = _iso.mapToIsoWorld(64, 0).x;
//the tile is a square in 3D space so the height matches the width
_tileHeight = _tileWidth;
buildGrid();
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
}
private function mouseMoved(e:MouseEvent):void {
if (_lastTile != null) {
_lastTile.alpha = 1;
_lastTile = null;
}
var coord:Coordinate = _iso.mapToIsoWorld(mouseX, mouseY);
var col:int = Math.floor(coord.x / _tileWidth);
var row:int = Math.floor(Math.abs(coord.z / _tileHeight));
if (col < _cols && row < _rows) {
var tile:Tile = getTile(col, row);
tile.alpha = .5;
_lastTile = tile;
}
}
private function buildGrid():void{
_grid = [];
_cols = 10;
_rows = 10;
for (var i:int = 0; i < _cols;++i) {
_grid[i] = [];
for (var j:int = 0; j < _rows;++j) {
var t:Tile = new Tile();
var tx:Number = i * _tileWidth;
var tz:Number = -j * _tileHeight;
var coord:Coordinate = _iso.mapToScreen(tx, 0, tz);
t.x = coord.x;
t.y = coord.y;
_grid[i][j] = t;
addChild(t);
}
}
}
private function getTile(col:int, row:int):Tile {
return _grid[col][row];
}
}
}
Then we have the isometric class that calculates 3d space.
package com.gamebook.utils {
import com.gamebook.utils.geom.Coordinate;
/**
* #author Jobe Makar - jobe#electrotank.com
*/
public class Isometric {
//trigonometric values stored for later use
private var _sinTheta:Number;
private var _cosTheta:Number;
private var _sinAlpha:Number;
private var _cosAlpha:Number;
/**
* Isometric class contrustor.
* #param declination value. Defaults to the most common value, which is 30.
*/
public function Isometric() {
var theta:Number = 30;//even though the tiles are already isometric, you still have to put the degrees the tiles will be turned.
var alpha:Number = 45;//45 degrees on y axis, 30 dgrees on x axis
theta *= Math.PI/180; // then you translate to radians
alpha *= Math.PI/180;
_sinTheta = Math.sin(theta);
_cosTheta = Math.cos(theta);
_sinAlpha = Math.sin(alpha);
_cosAlpha = Math.cos(alpha);
}
/**
* Maps 3D coordinates to the 2D screen
* #param x coordinate
* #param y coordinate
* #param z coordinate
* #return Coordinate instance containig screen x and screen y
*/
public function mapToScreen(xpp:Number, ypp:Number, zpp:Number):Coordinate {
var yp:Number = ypp;
var xp:Number = xpp*_cosAlpha+zpp*_sinAlpha;
var zp:Number = zpp*_cosAlpha-xpp*_sinAlpha;
var x:Number = xp;
var y:Number = yp*_cosTheta-zp*_sinTheta;
return new Coordinate(x, y, 0);
}
/**
* Maps 2D screen coordinates into 3D coordinates. It is assumed that the target 3D y coordinate is 0.
* #param screen x coordinate
* #param screen y coordinate
* #return Coordinate instance containig 3D x, y, and z
*/
public function mapToIsoWorld(screenX:Number, screenY:Number):Coordinate {
var z:Number = (screenX/_cosAlpha-screenY/(_sinAlpha*_sinTheta))*(1/(_cosAlpha/_sinAlpha+_sinAlpha/_cosAlpha));
var x:Number = (1/_cosAlpha)*(screenX-z*_sinAlpha);
return new Coordinate(x, 0, z);
}
}
}