I am building a flappy bird style side scroller game and currently implementing collectible items for the main sprite to collect as it flies. I am not moving the main sprite but moving the background using ParallaxEffect and intend to move the collectibles (called orbs) towards the main sprite (bird). The Orbs are rendered in random positions but the position is not changed even after the update method is called.
Here is my CollectibleOrbs.java
public class CollectibleOrbs {
private static final int ORB_COUNT = 10;
private Array<Orb> orbs;
private Orb orb;
public CollectibleOrbs(){
orbs = new Array<Orb>();
for(int i=0;i<ORB_COUNT; i++) {
orb = new Orb();
orbs.add(orb);
}
}
public void update(float delta){
for(Orb orb: orbs){
orb.update(delta);
}
}
public void render(SpriteBatch sb){
for(Orb orb:orbs){
orb.draw(sb);
}
}
private class Orb{
private ParticleEffect effect;
private Vector2 position;
private Random rand;
public Orb(){
effect = new ParticleEffect();
rand = new Random();
position = new Vector2(rand.nextInt(Gdx.graphics.getWidth()),rand.nextInt(Gdx.graphics.getHeight()));
effect.load(Gdx.files.internal("particle/orbred.p"),
Gdx.files.internal("particle"));
effect.setPosition(position.x,position.y);
}
public void draw(SpriteBatch sb){
effect.draw(sb,Gdx.graphics.getDeltaTime());
}
public void update(float dt){
if(position.x< 10){
position.x = rand.nextInt(Gdx.graphics.getWidth());
position.y = rand.nextInt(Gdx.graphics.getHeight());
}
else
{
position.x-= 100*dt;
}
}
}
}
The orbs are rendered but they are not moving whereas the bird animation and the parallax background does:
I am calling the update method of CollectibleOrb class in the update of my game state and respectively for the render method while passing required parameters. How do to make sure the orbs move on the game screen?
The problem is that position is just unrelated to effect vector. Changing just position won't change effect's position. One way to solve it:
public void update(float dt){
if(position.x< 10){
position.x = rand.nextInt(Gdx.graphics.getWidth());
position.y = rand.nextInt(Gdx.graphics.getHeight());
}
else
{
position.x-= 100*dt;
}
// you should update ParticleEffect position too, just like you did in the constructor
effect.setPosition(position.x, position.y);
}
Related
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?
How can I call the codes, inside the Update function using a public method?
What I need to archive is, calling Update using another function.
So that Update triggers using that other method.
One more thing, code should run only when a button long press.
Many Thanks four help
using UnityEngine;
using System.Collections;
public class CarController : MonoBehaviour
{
public float speed = 1500f;
public float rotationSpeed = 15f;
public WheelJoint2D backWheel;
public WheelJoint2D frontWheel;
public Rigidbody2D rb;
private float movement = 0f;
private float rotation = 0f;
void Update()
{
rotation = Input.GetAxisRaw("Horizontal");
movement = -Input.GetAxisRaw("Vertical") * speed;
}
void FixedUpdate()
{
if (movement == 0f)
{
backWheel.useMotor = false;
frontWheel.useMotor = false;
}
else
{
backWheel.useMotor = true;
frontWheel.useMotor = true;
JointMotor2D motor = new JointMotor2D { motorSpeed = movement, maxMotorTorque = 10000 };
backWheel.motor = motor;
frontWheel.motor = motor;
}
rb.AddTorque(-rotation * rotationSpeed * Time.fixedDeltaTime);
}
//public void Rotate()
//{
// rotate = true;
// print("aa");
//}
//public void Move()
//{
// rotation = Input.GetAxisRaw("Horizontal");
// movement = -Input.GetAxisRaw("Vertical") * speed;
//}
}
Those are actually two questions.
1. A long press button
The Unity UI.Button hasn't per se a method for a long press but you can use the IPointerXHandler interfaces for implementing that on your own:
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
// RequireComponent makes sure there is Button on the GameObject
[RequireComponent(typeof(Button))]
public class LongPressButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
private void Awake()
{
ResetButton();
}
// set the long press duration in the editor (in seconds)
public float LongPressDuration = 0.5f;
// Here you reference method just like in onClick
public UnityEvent onLongPress;
private float _timer;
private bool _isPressed;
private bool _pressInvoked;
private void Update()
{
// prevent multiple calls if button stays pressed
if (_pressInvoked) return;
// if button is not pressed do nothing
if (!_isPressed) return;
// reduce the timer by the time passed since last frame
_timer -= Time.deltaTime;
// if timeout not reached do nothing
if (!(_timer <= 0)) return;
// Invoke the onLongPress event -> call all referenced callback methods
onLongPress.Invoke();
_pressInvoked = true;
}
// reset all flags and timer
private void ResetButton()
{
_isPressed = false;
_timer = LongPressDuration;
_pressInvoked = false;
}
/* IPointer Callbacks */
// enable the timer
public void OnPointerDown(PointerEventData eventData)
{
_isPressed = true;
}
// reset if button is released before timeout
public void OnPointerUp(PointerEventData eventData)
{
ResetButton()
}
// reset if cursor leaves button before timeout
public void OnPointerExit(PointerEventData eventData)
{
ResetButton();
}
}
This script has to be placed next to the Button component.
You don't reference the callback method(s) in the Button's
onClick but instead in this LongPressButton's onLongPress
and don't forget to adjust LongPressDuration also in the inspector.
Example
2. Calling CarController's Update
I don't know why you want this (I guess you disabled the component but want to call Update anyway)
Solution A
In order to be able to reference that method in the Inspector there are a few options:
Simply make your Update method public
public void Update()
{
rotation = Input.GetAxisRaw("Horizontal");
movement = -Input.GetAxisRaw("Vertical") * speed;
}
wrap the content of Update in another public method and use that one instead:
private void Update()
{
DoUpdateStuff();
}
public void DoUpdateStuff()
{
rotation = Input.GetAxisRaw("Horizontal");
movement = -Input.GetAxisRaw("Vertical") * speed;
}
the other way round (how you requested it) - call Update from another public method:
private void Update()
{
rotation = Input.GetAxisRaw("Horizontal");
movement = -Input.GetAxisRaw("Vertical") * speed;
}
public void DoUpdateStuff()
{
Update();
}
So all that's left is referencing the CarController's Update or DoUpdateStuff method in the LongPressButton's onLongPress event.
Solution B
Alternatively you could add that callback directly on runtime without referencing anything nor making the callback method public so you could directly use private void Update without a wrapper method.
Drawback: For this method you somehow have to get the reference to that LongPressButton in your CarController script instead
public class CarController : MonoBehaviour
{
// Somehow get the reference for this either by referencing it or finding it on runtime etc
// I will asume this is already set
public LongPressButton longPressButton;
private void Awake()
{
// make sure listener is only added once
longPressButton.onLongPress.RemoveListener(Update);
longPressButton.onLongPress.AddListener(Update);
}
private void Update()
{
rotation = Input.GetAxisRaw("Horizontal");
movement = -Input.GetAxisRaw("Vertical") * speed;
}
private void OnDestroy()
{
// clean up the listener
longPressButton.onLongPress.RemoveListener(Update);
}
//...
}
Thanks for your descriptive reply. What I did was change my script to following and added Event Trigger component. Then call the public functions accordingly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CarMve : MonoBehaviour
{
bool move = false;
bool moveR = false;
public Rigidbody2D rb;
public float speed = 20f;
public float rotationSpeed = 2f;
private void FixedUpdate()
{
if (move == true)
{
rb.AddForce(transform.right * speed * Time.fixedDeltaTime * 100f, ForceMode2D.Force);
}
if (moveR == true)
{
rb.AddForce(transform.right *- speed * Time.fixedDeltaTime * 100f, ForceMode2D.Force);
}
}
/* Will be used on the UI Button */
public void MoveCar(bool _move)
{
move = _move;
}
public void MoveCarR(bool _moveR)
{
moveR = _moveR;
}
}
I am making a game in libgdx, where you shoot aliens with bullets. I have 2 ArrayLists of objects and would like to check if any of objects in bulletArrayList is colliding with any object from alienArrayList. What is the best way to do that? I was thinking of contactListener.
In the screen class I am generating objects like this:
public class PlayScreen implements Screen, InputProcessor,ContactListener {
public ArrayList<Alien> alienArrayList = new ArrayList<Alien>();
public ArrayList<Bullet> bulletArrayList = new ArrayList<Bullet>();
public void generateAlien() {
alien = new Alien();
alienArrayList.add(alien);
}
public void shootBullet(float x, float y) {
//send x,y moving coordiantes
bullet = new Bullet(x,y);
bulletArrayList.add(bullet);
}
}
In object class I have Rectangle box which i am moving like this:
public class Alien {
public Alien() {
bound = new Rectangle( x, y, alienRegion.getRegionWidth(), alienRegion.getRegionHeight());
}
public void update(float delta) {
bound.y -= speed * delta;
}
public void render(SpriteBatch batch, float delta) {
update(delta);
elapsedTime += Gdx.graphics.getDeltaTime();
alienRegion = (TextureRegion) alien.getKeyFrame(elapsedTime, true);
batch.draw(alienRegion, getBound().x, getBound().y);
}
}
Because you are using Rectangles in your Alien class, we can use a class called Intersector which has static methods to check for collision detection.
for(Alien alien1: alienArrayList) {
for(Alien alien2 : bulletArrayList) {
if(Intersector.overlaps(alien1.rect, alien2.rect)) {
// Collision code
}
}
}
First, we iterate through the two lists using a nested special for loop. Then we pass two Rectangles to the Intersector.overlaps(rect1, rect2). This is a static method defined in the Intersector class which will return true if the rectangles are overlapping.
Also, this code can go straight into your render method.
This code is not the most optimized because it will check 2 rects twice however, I will leave the optimization to you.
I hope that this answer was helpful and if you have any further questions please feel free to post a comment below.
I am trying to make a sprite move, rotate and resize by using MoveToAction, RotateToAction and ScaleToAction. The first two works fine, but I have a problem with ScaleToAction.
I add the action to the Actor just like I do with the two that works. I think the problem might be in the #Override? When I run the code the sprite moves and rotates but no scaling is done.
I tried to use sprite.setscale as suggested in the answer below, but still no luck. I add the code from the class here:
public class Prizes extends Actor {
private LearnToRead game;
private TextureAtlas atlas;
private TextureRegion prizepic;
private Sprite sprite;
private RotateToAction rta;
private ScaleToAction sta;
private MoveToAction mta;
public Prizes(LearnToRead game) {
this.game = game;
atlas = new TextureAtlas("prizes.pack");
prizepic = atlas.findRegion("haxhatt");
sprite = new Sprite(prizepic);
sprite.setPosition(450 - sprite.getWidth() / 2, 450 * Gdx.graphics.getHeight() / Gdx.graphics.getWidth() - sprite.getHeight() / 2);
//setBounds(sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
setTouchable(Touchable.enabled);
rta = new RotateToAction();
sta = new ScaleToAction();
mta = new MoveToAction();
rta.setRotation(180f);
sta.setScale(2f);
mta.setPosition(0, 0);
mta.setDuration(5f);
rta.setDuration(5f);
sta.setDuration(5f);
Prizes.this.addAction(rta);
Prizes.this.addAction(sta);
Prizes.this.addAction(mta);
}
#Override
public void draw(Batch batch, float parentAlpha) {
sprite.draw(batch);
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(), getY());
}
#Override
protected void rotationChanged() {
sprite.setRotation(getRotation());
}
#Override
protected void sizeChanged() {
sprite.setScale(getScaleX(), getScaleY());
}
}
If also tried to remove sprite and just use the TextureRegion, but didn't get it to work. The texture is drawn, but not moving. I post that code as well, but I do confess that I am quite uncertain about this code:
public class Prizes extends Actor {
private LearnToRead game;
private TextureAtlas atlas;
private TextureRegion prizepic;
private RotateToAction rta;
private ScaleToAction sta;
private MoveToAction mta;
public Prizes(LearnToRead game) {
this.game = game;
atlas = new TextureAtlas("prizes.pack");
prizepic = atlas.findRegion("haxhatt");
Prizes.this.setBounds(450 - Prizes.this.getX(), Prizes.this.getY(), Prizes.this.getWidth(), Prizes.this.getHeight());
setTouchable(Touchable.enabled);
rta = new RotateToAction();
sta = new ScaleToAction();
mta = new MoveToAction();
rta.setRotation(180f);
sta.setScale(2f);
mta.setPosition(0, 0);
mta.setDuration(5f);
rta.setDuration(5f);
sta.setDuration(5f);
Prizes.this.addAction(rta);
Prizes.this.addAction(sta);
Prizes.this.addAction(mta);
}
#Override
public void draw(Batch batch, float parentAlpha) {
game.batch.begin();
game.batch.draw(prizepic, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
game.batch.end();
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
protected void positionChanged() {
Prizes.this.setPosition(getX(), getY());
}
#Override
protected void rotationChanged() {
Prizes.this.setRotation(getRotation());
}
#Override
protected void sizeChanged() {
Prizes.this.setScale(getScaleX(), getScaleY());
}
}
Maybe someone has a good idea about what I am doing wrong?
Use sprite.setScale instead of sprite.scale. The difference is that you are setting it a specific value instead of multiplying the current scale by some value.
But it is redundant to use Sprite with Actor because both classes store position, rotation, scale, and color. It makes more sense to use a TextureRegion with Actor. Or you can use the Image class, which already handles this for you.
Edit:
I see the other part of the issue. You are overriding sizeChanged, but it's the scale, not the size, that you are changing with a ScaleToAction. Actor doesn't have a callback for the scale changing. You could override the setScale methods to apply your change to the Sprite, but like I said above, it doesn't make sense to be using a Sprite for this. You should reference a TextureRegion, and draw it with all the appropriate parameters in the draw() method.
I want to make a text appear in the center of the screen, indicating
the current level. It should appear gradually and after a while disappear gradually. I'm using scene2d with stages, actors.. so i would use Actions.
This is what i have now:
public class TextActor extends Actor {
private BitmapFont font;
private CharSequence charSequence;
public TextActor(CharSequence charSequence) {
font = new BitmapFont(Gdx.files.internal("fonts/white_standard_font.fnt"));
this.charSequence = charSequence;
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
public void draw(Batch batch, float delta) {
super.draw(batch, delta);
font.draw(batch, charSequence, getX(), getY());
}
}
In the class that creates the TextActor..
textActor.addAction(Actions.sequence(Actions.fadeIn(1f), Actions.delay(1f), Actions.fadeOut(1f), new Action() {
#Override
public boolean act(float delta) {
textActor.remove();
transitionInProgress = false;
gameState = GameState.RUNNING;
Gdx.input.setInputProcessor(stage);
return true;
}
}));
gameTable.addActor(textActor);
fadeIn, fadeOut, alpha.. don't work. I tried with "moveBy" and it works, so it seems a problem concerning the appearance of the Actor. There is something that escapes me.
The fade actions modify the alpha value of the Actor's color (getColor().a). You're drawing the font directly without applying the color associated with the actor.
Take a look at how Label.draw is implemented for a better understanding. In the meantime, just try adding this above your font.draw(...) call:
font.setColor(getColor())
Or, if you don't want to modify the entire color, just the alpha, try this:
font.getColor().a = getColor().a;
UPDATE:
Also note that you should apply the parentAlpha (second parameter of draw - labelled as delta in your example) to the final alpha:
font.getColor().a = getColor().a * parentAlpha
This allows your actor to fade if you change the alpha of the stage or any parents.