in my libgdx tiles 2D jump and run game I want to create Bodies every second at the positin of my cannons, but as far as I am, only one cannon shoots every second.
Can anyone help me how to fix this?
Heres where I create my cannons:
//create cannon bodies/fixtures
cannons = new Array<Cannon>();
for (MapObject object : map.getLayers().get(5).getObjects().getByType(RectangleMapObject.class)) {
recta = ((RectangleMapObject) object).getRectangle();
cannons.add(new Cannon(screen, recta.getX(), recta.getY()));
}
}
public float X() {
return recta.getX();
}
public float Y() {
return recta.getY();
}
Here I add canons( that works for every position) :
private Array<Bullet> bullets;
public Cannon(PlayScreen screen, float x, float y) {
super(screen, x, y);
setBounds(getX(), getY(), getWidth(), getHeight());
}
public Cannon() {
super();
}
#Override
protected void defineTrap() {
b2body = Body.addStatic(b2body, world, 0f, getX(), getY(), 32, x.CANNON_BIT, "cannon");
createBullet();
}
public void createBullet() {
bullets = new Array<Bullet>();
bullets.add(new Bullet(screen, getX(), getY()));
}
Here I add the first bullet(that waoks too for all cannons):
public Bullet(PlayScreen screen, float x, float y) {
super(screen, x, y);
setBounds(getX(), getY(), getWidth(), getHeight());
}
public Bullet() {
}
#Override
public void defineTrap() {
b2body = Body.addDynamic(b2body, world, 0f, getX(), getY(), 8, x.BULLET_BIT, "bullet");
b2body.setLinearVelocity(new Vector2(-1f, 0));
b2body.setGravityScale(0);
}
And here I want every cannon to shoot, but only one does (its my render mehtod):
if (TimeUtils.timeSinceNanos(startTime) > 1000000000) {
bullets = new Array<Bullet>();
bullets.add(new Bullet(this, creator.X(), creator.Y()));
startTime = TimeUtils.nanoTime();
}
I hope you can help me!
You should iterate through all cannons and call some method that can access the private field bullets, I called it addBullet(Screen, float, float). You might to need to do something with creator before addBullet() as I'm unsure what you are doing with it.
if (TimeUtils.timeSinceNanos(startTime) > 1000000000) {
for(int i = 0; i < Cannons.size; i++) {
Cannon cannon = cannons.get(i);
cannon.addBullet(this, creator.X(), creator.Y());
}
startTime = TimeUtils.nanoTime();
}
Then create the addBullet method in the Cannon class.
public void addBullet(Screen screen, float x, float y) {
bullets.add(new Bullet(screen, x, y));
}
As far as I can tell there is no reason to reinitialize the bullet array every time as you have been doing. Just use pop(); or removeIndex(index); to remove the array items that are no longer used. You could add some more methods to the Cannon class
public void removeLastBullet() {
bullets.pop();
}
public void removeBullet(int i) {
bullets.removeIndex(i);
}
Related
I wanted to create a simple 30x30 isometric tiled map and add a listener to be able to click on the tiles. I looked up a lot of the articles and posts here but none of them helped me.
My issue is, i have a viewport, 30x17, a camera, a stage and a tiled map with tileWidth = 32 and tileHeight = 16 pixels.
Now when i render the tiled map it looks fine.
When i click on stage and try to get the world coordinates i see some really weird coordinates.
This is the code:
private static final float TILE_WIDTH = 32;
private static final float TILE_HEIGHT = 16;
private OrthographicCamera camera;
private Viewport viewport;
private Stage stage;
private IsometricTiledMapRenderer isometricTiledMapRenderer;
private Matrix4 isoTransform;
private Matrix4 invIsotransform;
public void load(AssetManagerLoaderV2 assetManagerLoader) {
assetManagerLoader.load();
init();
camera = new OrthographicCamera();
viewport = new FitViewport(30, 17, camera);
stage = new Stage(viewport);
TiledMap tiledMap = new TiledMapGenerator(assetManagerLoader).generate(30, 30);
isometricTiledMapRenderer = new IsometricTiledMapRenderer(tiledMap, 1/32f);
stage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println(screenToCell(x, y));
return true;
}
});
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
DrawUtils.clearScreen();
viewport.apply();
isometricTiledMapRenderer.setView(camera);
isometricTiledMapRenderer.render();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
isometricTiledMapRenderer.dispose();
stage.dispose();
}
public void init () {
//create the isometric transform
isoTransform = new Matrix4();
isoTransform.idt();
isoTransform.translate(0.0f, 0.25f, 0.0f);
isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
isoTransform.rotate(0.0f, 0.0f, 1.0f, -45.0f);
//... and the inverse matrix
invIsotransform = new Matrix4(isoTransform);
invIsotransform.inv();
}
public Vector2 worldToCell(float x, float y) {
float halfTileWidth = TILE_WIDTH * 0.5f;
float halfTileHeight = TILE_HEIGHT * 0.5f;
float row = (1.0f/2) * (x/halfTileWidth + y/halfTileHeight);
float col = (1.0f/2) * (x/halfTileWidth - y/halfTileHeight);
return new Vector2((int)col,(int)row);
}
public Vector2 screenToWorld(float x, float y){
Vector3 touch = new Vector3(x,y,0);
camera.unproject(touch);
touch.mul(invIsotransform);
touch.mul(isoTransform);
return new Vector2(touch.x,touch.y);
}
public Vector2 screenToCell(float x, float y) {
Vector2 world = screenToWorld(x,y);
world.y -= TILE_HEIGHT *0.5f;
return worldToCell(world.x,world.y);
}
Does anyone have an idea how to write worldToCell to get the proper coordinates?
I found the issue, i was missing one step.
This is what touchDown should look like:
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Vector2 newCoords = stage.stageToScreenCoordinates(new Vector2(x, y));
System.out.println(screenToCell(newCoords.x, newCoords.y));
return true;
}
I need to convert stage coordinates to screen coordinates, now this code is working.
I have a gameObject called BounceBack that is supposed to bounce the ball back far away when they collide together.
public class BounceBack : MonoBehaviour
{
public GameObject Player;
public float force;
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag(Player.tag))
{
Player.GetComponent<PlayerController>().ForceBack(force);
}
}
}
The ball Player (ball) script:
public class PlayerController : MonoBehaviour
{
public int acceleration;
public int speedLimit;
public int sideSpeed;
public Text countText;
public Text winText;
public GameObject pickUp;
public GameObject finishLine;
//internal void ForceBack() //Not sure what it does and why it's there.
//{
// throw new NotImplementedException();
//}
private int count;
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
SetCount();
}
// Update is called once per frame
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal") * sideSpeed * rb.velocity.magnitude / acceleration;
//float moveVertical = Input.GetAxis("Vertical") * acceleration;
if (rb.velocity.magnitude <= speedLimit)
{
rb.AddForce(0.0f, 0.0f, acceleration); // add vertical force
}
rb.AddForce(moveHorizontal, 0.0f, 0.0f); // add horizontal force
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag(pickUp.tag))
{
other.GetComponent<Rotate>().Disapear();
count++;
SetCount();
}
if (other.gameObject.CompareTag(finishLine.tag))
{
acceleration = 0;
sideSpeed = 0;
finishLine.GetComponent<GameEnd>().FadeOut();
if (count >= 2)
{
winText.GetComponent<WinTextFadeIn>().FadeIn("Vous avez remporté la partie!");
}
else
{
winText.GetComponent<WinTextFadeIn>().FadeIn("Vous avez perdu. Réesayer?");
}
}
}
private void SetCount()
{
countText.text = "Count : " + count.ToString();
}
public void ForceBack(float force)
{
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(0.0f, 0.0f, -force, ForceMode.VelocityChange);
Debug.Log("Pass");
}
}
The AddForce function does not do anything. I tried with setActive(false) and it's not working either. The only thing that works is Debug.Log(). I'm not sure if the speedlimit and acceleration are interfering with the function.
EDIT: I'm not sure if the problem is from Unity but I can't access any variable of the class from the forceBack function inside the class.
EDIT2: I also tried to call the AddForce function directly in the Bounce Back script but it's not working either.
Player.GetComponent<Rigidbody>().AddForce(0.0f, 0.0f, -force, ForceMode.VelocityChange);
Player (Ball) Screenshot
Bounce Back Screenshot
So, a couple things:
1.) The physics system should already cause the ball to bounce if you've set up the colliders and rigidbodies properly. You should only need to do something like this if the ball should gain momentum when it bounces, which is unlikely. You should post screenshots of their inspectors if this answer doesn't help.
2.) On your rb.AddForce() call, you're applying a force in world-space, which may be the wrong direction to bounce. If you know the ball is oriented the way it's moving, then you can call AddRelativeForce with the same parameters. If the ball's orientation is not controlled, then you need to calculate the correct world-space direction to use before applying the force.
3.) Finally, just to confirm, the objects with BounceBack attached do have a non-zero value in the 'force' parameter in the inspector, right?
I'am having a issue or just don't know how to make it to work so everything is seen in image.
rotation = lerp(rotation, realRotation, delta / 2);
IMAGE GIF
You want to linearly interpolate ship rotation between a particular angle(set by TouchPad) to original(default) value.
I am trying to achieve your requirement in my code.
public class TestGame1 extends Game {
Stage stage;
Touchpad touchpad;
Image image;
float angle=0;
Texture shipTex;
#Override
public void create() {
stage=new Stage();
image=new Image(shipTex=new Texture("ship1.png"));
image.setPosition(300,300);
image.setSize(100,107);
image.setOrigin(50,53);
Skin skin=new Skin(Gdx.files.internal("skin/uiskin.json"));
touchpad=new Touchpad(10,skin);
touchpad.setPosition(100,100);
Gdx.input.setInputProcessor(stage);
stage.addActor(image);
stage.addActor(touchpad);
}
#Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(0,0,0,1);
stage.act();
stage.draw();
float knobX=touchpad.getKnobPercentX();
float knobY=touchpad.getKnobPercentY();
if(knobX!=0 && knobY!=0) {
float radAngle = MathUtils.atan2(knobY, knobX);
angle = MathUtils.radiansToDegrees * radAngle;
angle -= 90;
if (angle > 360)
angle -= 360;
}
else
angle=MathUtils.lerpAngleDeg(angle,0,Gdx.graphics.getDeltaTime());
image.setRotation(angle);
}
#Override
public void dispose() {
stage.dispose();
shipTex.dispose();
}
}
What i want:
I want allow the user to drag and drop the actors he puts on the screen
1) An actor (32x32 pixel) can be put in a certain area defined by me (a centered rectangle 5x5, 32 pixel each cell)
2) The actors are snapped to an imaginary grid of the screen when added to the stage.
3) This snap to grid is needed also during the drag and drop
4) An actor can't be moved on another one
5) An actor must be rotated by 90 degrees when the user clicks on it
6) The actor can't be moved outside the bounds
What actually i get:
1) ok
2) ok
3) ok (with bugs)
4) not implemented yet
5) ok (with bugs)
6) not implemented yet
The bugs are:
1) The rotations is applied in any case. I don't want this behavior. I don't want to rotate an actor if the user is just doing drag and drop. (i know that this happens because i put this part of the code in the touchDown method)
2) This is hard to describe with my english, i'll try.
I put an actor and i can move it (when i say "move" i mean drag and drop).
I put a second actor and i can move it.
Now, if i try to move the first actor i put, the second one moves over the first (overlapping to it) and starts moving along with it!
Now the code:
Events in the GameStage
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
translateScreenToWorldCoordinates(screenX, screenY);
// check if the user clicked or not in the center area
if(!centerArea.contains(touchPoint.x, touchPoint.y)) {
return true;
}
// rotation
Iterator<Square> squareIterator = squares.iterator();
while(squareIterator.hasNext()) {
Square nextSquare = squareIterator.next();
if(nextSquare.getBounds().contains(touchPoint.x, touchPoint.y)) {
nextSquare.rotate();
return true;
}
}
// add an Actor
touchPoint.set(touchPoint.x - Constants.SQUARE_SIZE / 2, touchPoint.y - Constants.SQUARE_SIZE / 2, 0f);
float snapX = Math.round((touchPoint.x / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
float snapY = Math.round((touchPoint.y / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
Square square = new Square(snapX, snapY);
squares.add(square);
addActor(square);
return true;
}
public boolean touchDragged(int screenX, int screenY, int pointer) {
Vector3 newTouchPoint = new Vector3(screenX, screenY, 0f);
getCamera().unproject(newTouchPoint.set(screenX, screenY, 0f));
Square next = null;
Iterator<Square> squareIterator = squares.listIterator();
while(squareIterator.hasNext()) {
next = squareIterator.next();
if(next.getBounds().contains(newTouchPoint.x, newTouchPoint.y)) {
next.setX(newTouchPoint.x - Constants.SQUARE_SIZE / 2);
next.setY(newTouchPoint.y - Constants.SQUARE_SIZE / 2);
continue;
}
}
newTouchPoint.set(newTouchPoint.x - Constants.SQUARE_SIZE / 2, newTouchPoint.y - Constants.SQUARE_SIZE / 2, 0f);
float snapX = Math.round((newTouchPoint.x / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
float snapY = Math.round((newTouchPoint.y / Constants.SQUARE_SIZE)) * Constants.SQUARE_SIZE;
next.setX(snapX);
next.setY(snapY);
return true;
}
private void translateScreenToWorldCoordinates(int x, int y) {
getCamera().unproject(touchPoint.set(x, y, 0f));
}
Actor
public class Square extends GameActor {
private float rotation;
private ShapeRenderer shapeRenderer;
public Square(float x, float y) {
super(x, y);
shapeRenderer = new ShapeRenderer();
}
#Override
protected void initializeSprite() {
texture = new Texture(Gdx.files.internal(Constants.SQUARE_LOCATION));
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
protected void updateBounds() {
bounds.setPosition(x, y);
}
public void rotate() {
rotation -= 90;
if(rotation <= -360) {
rotation = 0;
}
setRotation(rotation);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
/*shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(Color.RED);
shapeRenderer.rect(bounds.getX(), bounds.getY(), Constants.SQUARE_SIZE, Constants.SQUARE_SIZE);
shapeRenderer.end();*/
//batch.begin();
batch.draw(texture, x, y, texture.getWidth() / 2, texture.getHeight() / 2,
texture.getWidth(), texture.getHeight(), 1, 1, rotation, 0, 0, Constants.SQUARE_SIZE, Constants.SQUARE_SIZE, false, false);
}
}
I hope you can help me, i'm new to Libgdx, thank you.
UPDATE
I fixed the rotation bug moving its logic inside the touchUp method.
Hello I've been stuck on an issue for a while trying to create a simple game using the Libgdx framework.
I have a stage with a background Image actor and a stick figure image actor that is placed in the center of the stage.
I cant seem to understand, after reading many posts on stackoverflow and the libgdx wiki on event handling, how events such as fling and pan are passed to the actor to simply move it my stickman around the screen.
I would greatly appreciate any help or explanations on how exactly this works.
I just don't know how to get the integer values from pan and the velocity values from fling into my actor's Vector2 position variable so that it can move around every time the render method is called.
Here is some of my code:
My GestureDetection Class:
public class GestureDetection implements GestureListener{
public GestureDetection(){
}
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
System.out.println("tapped");
return false;
}
#Override
public boolean longPress(float x, float y) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean panStop(float x, float y, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean zoom(float initialDistance, float distance) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
Vector2 pointer1, Vector2 pointer2) {
// TODO Auto-generated method stub
return false;
}
}
My Game class
public class Game implements Screen{
StickFlick game;
SpriteBatch batch;
Texture gameBackground;
Stage stage;
GestureDetector gd;
InputMultiplexer im;
WalkingEnemy testEnemy;
public Game(StickFlick game){
this.game = game;
testEnemy = new WalkingEnemy("basic", 100, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//enemy1.update(delta);
System.out.println("");
stage.act(Gdx.graphics.getDeltaTime());
batch.begin();
stage.draw();
batch.end();
}
#Override
public void resize(int width, int height) {
stage = new Stage(width, height, true);
stage.clear();
gd = new GestureDetector(new GestureDetection());
im = new InputMultiplexer(gd, stage);
Gdx.input.setInputProcessor(im);
Texture gameBackground = new Texture("data/gameBackground.png");
Image backgroundImage = new Image(gameBackground);
backgroundImage.setWidth(Gdx.graphics.getWidth());
backgroundImage.setHeight(Gdx.graphics.getHeight());
stage.addActor(backgroundImage);
stage.addActor(testEnemy.getImage());
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(1)));
}
#Override
public void show() {
batch = new SpriteBatch();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
Ok, so at start you have to make some variables where you will keep X and Y coords of your stick man. Set some initial values to them, i.e. x should be half of screen width and y half of screen height to have your stick man at the center of the screen.
Then you have to use information you are getting in your GestureListener implementation to move your stick man, that is to change it's coordinates. Your current implementation does absolutely nothing - methods are empty, so when some gesture happens your code is not reacting to it.
So, you can i.e. inside of your pan method use deltaX and deltaY to change stick man coordinates for those values. Something like:
X+=deltaX;
y+=deltaY;
Or you can multiply deltaX and deltaY with some constant if you want faster or slower movement and/or multiply deltaY with -1 if movement is in opposite way from what you want it to be.
Or if you want to use fling use those variables that method provides - velocityX and velocityY - add them to you stick man x and y to move it.
So basically, you are always drawing stick man at x,y coords and from some method of GestureListener interface you implemented you should change stick man coords and make him move.