This is a greenfoot project I'm working on. It is supposed to make the object bounce when it nears the edge
I was hoping someone would realize why this isn't working, it just falls from the top
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Asteroid extends Actor
{
/**
* Act - do whatever the Asteroid wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
boolean direction=true; //assigns the fall direction (true is down false is up)
int acceleration =0;
public void act()
{
if(getY()>(getWorld().getHeight())-50 && direction== true )
//checks if it is near the bottom of the world (Y is reversed so at the top of the world Y is high)
{
direction= false;
acceleration = 0; //Resets speed
}
if(getY()<50 && direction== false)
{
direction= true;
acceleration = 0;
}
if(direction=true)
{
setLocation(getX(), getY()+(int)(Greenfoot.getRandomNumber(25)+acceleration));
}
else if(direction=false)
{
setLocation(getX(), getY()-(int)(Greenfoot.getRandomNumber(25)+acceleration));
}
acceleration++;
}
}
You need to change your direction at boundaries. Instead of storing it as boolean (true,false), store it as (1,-1) and change it at boundaries.
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
public class Asteroid extends Actor
{
int direction=1;
int acceleration=0;
public void changeDirection()
{
direction = direction * -1;
}
public void resetAcceleration()
{
acceleration=0;
}
public int getAcceleration()
{
int value = (Greenfoot.getRandomNumber(25) + acceleration)* direction;
return value;
}
public void act()
{
if(getY()>(getWorld().getHeight())-50 && direction > 0 )
{
changeDirection();
resetAcceleration();
}
if(getY()<50 && direction < 0)
{
changeDirection();
resetAcceleration();
}
setLocation(getX(), getY()+getAcceleration());
acceleration++;
}
}
This code is the problem:
if(direction=true)
This will assign true to direction; you need double-equals there to check for equality:
if (direction == true)
It's annoying that Java allows this. Same problem for the else clause on the if.
Related
So I made a snake game in unity but whenever the snake grows the segment that should be added to the snake dissappears when i change the direction and idk why.
Here's the code:
private void FixedUpdate()
{
for (int i = _segments.Count - 1; i > 0; i--)
{
_segments[i].position = _segments[i - 1].position;
}
this.transform.position = new Vector2(
Mathf.Round(this.transform.position.x) + sobolan.x,
Mathf.Round(this.transform.position.y) + sobolan.y
);
}
private void Grow()
{
Transform segment = Instantiate(this.segmentPrefab);
segment.position = _segments[_segments.Count - 1].position;
_segments.Add(segment);
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Food")
{
Grow();
}
}
I'm not entirely sure how to phrase question so sorry if this is confusing. Anyways for context I'm making a sort of minesweeper type of game in unity and one of the things the original game had was a timer. Here it is. I want to copy that sort of thing, and while I do have code that works, it's honestly kind of redundant here's what I have .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Timer : MonoBehaviour
{
public float timer = 0;
public bool isStop = false;
public Image scoreCount;
public Sprite[] numbersprite;
// Start is called before the first frame update
void Start()
{
timer = 0;
isStop = true;
}
Ignore all the stuff on the top.
// Update is called once per frame
void Update()
{
if(!isStop)
{
timer += Time.deltaTime;
if(timer >= 1f)
{
scoreCount.sprite = numbersprite[1];
}
if(timer >= 2f)
{
scoreCount.sprite = numbersprite[2];
}
if(timer >= 3f)
{
scoreCount.sprite = numbersprite[3];
}
if(timer >= 4f)
{
scoreCount.sprite = numbersprite[4];
}
if(timer >= 5f)
{
scoreCount.sprite = numbersprite[5];
}
if(timer >= 6f)
{
scoreCount.sprite = numbersprite[6];
}
}
}
}
What I want is to make it so that it both displays a specific sprite after a certain amount of time has based but also not have to resort to using any of this. Is there any way I can make this work?
If you want some more information I can give you that.
This code is the ultimate solution to the problem and can support sprite indefinitely. It is also fully optimized. Just put the sprites 0 to 9 in the first list and the images in the second list respectively.
public Sprite[] spriteNumbers = new Sprite[10]; // fill with numbers
public List<Image> spriteFieds; // set Images Based on a unit of tens of hundreds
public void Start() => InvokeRepeating(nameof(SyncTimer), 0f, 1f);
public void SyncTimer()
{
for (var i = 0; i < spriteFieds.Count; i++)
{
var index = (int) (Time.time / Mathf.Pow(10, i) % 10);
spriteFieds[i].sprite = spriteNumbers[index];
}
}
How to make Stop Timer?
Here I created a standalone timer field and you can stop the step timer by pressing the Space key. You can also reset the timer with the R key, for example.
public Sprite[] spriteNumbers = new Sprite[10]; // fill with numbers
public List<Image> spriteFieds; // set timer Fields
public bool isStop;
public float timer;
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) isStop = !isStop;
if (Input.GetKeyDown(KeyCode.R))
{
timer = 0;
SyncTimer();
}
if (!isStop)
{
timer += Time.deltaTime;
SyncTimer();
}
}
public void SyncTimer()
{
for (var i = 0; i < spriteFieds.Count; i++)
{
var index = (int) (timer / Mathf.Pow(10, i) % 10);
spriteFieds[i].sprite = spriteNumbers[index];
}
}
The Timer Result:
I'm assuming you have 10 sprites for you numbers 0-9.
numberSprite[0] would hold the sprite for "0", numberSprite[1] would hole "1", etc.
Let's say the timer is at 319.8f seconds on the back end. You would want 3 sprites to display: 3, 1, 9.
To do this, you'll need to break your timer value and sprite into the hundredths, tenths, and seconds individually. You could do this:
int timerInt = (int)Mathf.floor(timer); //Get the int value of the timer
int hundredth = (timerInt/100) % 10; // (319/100) => 3 ... (3 % 10) => 3
scoreCountHundredths.sprite = numberSprite[hundredth];
int tenth = (timerInt /10) % 10; //(319/10) => 31 ... (31 % 10) => 1
scoreCountTenths.sprite = numberSprite[tenth];
int second = timerInt % 10; // (319 % 10) => 9
scoreCountSeconds.sprite = numberSprite[second];
With the above code, your timer should correctly update to any number between 000-999 requiring only 10 sprites uploaded. Additionally, it will automatically loop if your timer goes above 999 due to the modulo (%) logic.
Warning. Coroutines or InvokeRepeating may be a trap here:
Coroutines can be used to track the time between updating the sprites, but you'll likely be wanting to tie this display directly to the in-game time. relying on coroutines to update the sprite de-couples the in-game timer from the display, as they do not have built-in catchup behaviour. If your frames are slightly delayed or lag at all, you run the risk of the time running slower when using coroutines or InvokeRepeating.
Coroutines are perfect for this. Try this code here:
public Image image;
public Sprite[] sprites;
private bool isStop = true;
private void Start()
{
isStop = false;
StartCoroutine(Timer());
}
private IEnumerator Timer()
{
while (!isStop)
{
for (int i = 0; i < sprites.Length; i++)
{
if (isStop) break;
image.sprite = sprites[i];
yield return new WaitForSeconds(1f);
}
}
}
You can convert float to int
int spriteIndex = (int)Math.Round(timer);
And used spriteIndex as index to array sprites.
Or... if you need used different time interval for every sprite, you can make special struct for this animation.
For example:
[Serializable]
public struct SpriteFrame
{
public Sprite FrameSprite;
public float TimeToShow;
}
public class SpriteAnimationComponent : MonoBehaviour
{
public Image ScoreCount;
public List<SpriteFrame> Frames = new List<SpriteFrame>();
private int _currentFrame = 0;
private float _currentPlayAnimationTime = 0f;
private bool IsPlay => _currentFrame < Frames.Count;
public void Start()
{
UpdateFrame();
}
public void Update()
{
if(!IsPlay)
return;
_currentPlayAnimationTime += Time.deltaTime;
if(NeedShowNextFrame())
ShowNextFrame();
}
private bool NeedShowNextFrame()
=> Frames[_currentFrame].TimeToShow < _currentPlayAnimationTime;
private void ShowNextFrame()
{
_currentPlayAnimationTime -= Frames[_currentFrame].TimeToShow;
_currentFrame++;
if(IsPlay)
{
UpdateFrame();
}
}
private void UpdateFrame()
{
ScoreCount.sprite = Frames[_currentFrame].FrameSprite;
}
}
You need used SerializableAttribute ([Serializable]) on SpriteFrame for show struct in Unity Inspector. In current code animation show once, but you can make it loop. For loop animation just add _currentFrame %= Frames.Count after _currentFrame++
When the player hits the ground the camera shakes and when he jumps he just get's teleported up in an unsmooth manner
public class PlayerController : MonoBehaviour {
CharacterController controller;
Vector3 motion = Vector3.zero;
#region Movement Variables
public float walkingSpeed = 4f;
public float runningSpeed = 6f;
public float jumpSpeed = 5f;
public float gravity = 9.8f;
#endregion Movement Variables
void Start () {
controller = GetComponent<CharacterController>();
}
void Update () {
motion = Vector3.zero;
motion.x = Input.GetAxis("Horizontal");
if (controller.isGrounded && Input.GetAxis("Vertical")>0) motion.y += jumpSpeed;
if (Input.GetKey(KeyCode.LeftShift))
{
motion.x *= runningSpeed;
}
else
{
motion.x *= walkingSpeed;
}
motion.y -= gravity;
}
void FixedUpdate()
{
controller.Move(motion * Time.deltaTime);
}
}
I'm trying to create smooth movement and currently it is not reliable at all. thanks for any helpers
Take controller.Move(motion * Time.deltaTime); out of FixedUpdate and put it at the very end of Update method.
In case you have some script on camera as well, for example, some implementation of a follow camera where you're working with the transform component of that camera in the Update loop, that should go in LateUpdate instead.
See this https://learn.unity.com/tutorial/update-and-fixedupdate#5c8a4242edbc2a001f47cd63
and https://docs.unity3d.com/ScriptReference/MonoBehaviour.LateUpdate.html
This implementation of a player controller for GameObject that has a Character Controller component may also help you.
void Update()
{
if (isDead) return;
isWalking = Input.GetButton("Walk");
isGrounded = characterController.isGrounded;
if (isGrounded)
{
// Move
float verticalAxis = Input.GetAxis("Vertical");
moveDirection = new Vector3(0, 0, verticalAxis);
moveDirection = transform.TransformDirection(moveDirection);
if (verticalAxis > 0 && isWalking)
{
moveDirection *= walkingSpeed;
}
else if (verticalAxis > 0)
{
moveDirection *= runningSpeed;
}
// Jump
if (Input.GetButtonDown("Jump"))
{
moveDirection.y = jumpSpeed;
//moveDirection.z = jumpDistance; in case some forward boost is required
moveDirection = transform.TransformDirection(moveDirection);
}
}
// Gravity
moveDirection.y -= gravity * Time.deltaTime;
characterController.Move(moveDirection * Time.deltaTime);
// Rotation
float horizontalAxis = Input.GetAxis("Horizontal");
transform.Rotate(0, horizontalAxis, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.FromToRotation(transform.up, GetHitNormal()) * transform.rotation, yRotationSpeed * Time.deltaTime);
}
// To keep local up aligned with global up on a slope ground
private Vector3 GetHitNormal()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit))
return hit.normal;
else
return Vector3.zero;
}
Few days before, I asked you to help me because I couldn't show the drawing of a circle pixel by pixel (the drawing got stuck). #James_D found the solution and told me it was because a background-thread was trying to modify the UI. Indeed, I have a GraphicEngine class that extends Service : this class is this background-thread and its aim was to calculate and modify the image (now its aim is just to calculate the circle's pixels).
Well, finally, thanks to #James_D, I have now some classes :
GraphicEngine,
ImageAnimation which contains the animation of drawing,
DialogCreationOfCircularGradation, which is a dialog containing a button "OK, draw circle !" and which handles this event.
The below source contains the answer of my program to the user-event "Draw a circle". It gets several Textfield's input and give it to the GraphicsEngine. Moreover it asks the latter to do the calculation of the circle's pixels and asks the ImageAnimation to display the calculation pixel by pixel, during the calculation done by the GraphicsEngine.
CLASS DialogCreationOfCircularGradation
public void dialogHandleEvents() {
Optional r = this.showAndWait();
if(r.isPresent() && r.get() == ButtonType.OK) { // The user clicks on "OK, draw the circle !"
int radius = Integer.parseInt(this.dialog_field_radius.getText());
[...]
this.gui.getImageLoader().loadImageFromUsersPreferences(x0 + thickness + 2*radius, y0 + thickness + 2*radius);
this.gui.getGraphicEngine().setOperationToDo("Circle");
this.gui.getGraphicEngine().setRadius(radius);
[...]
this.gui.getGraphicEngine().restart();
this.gui.getImageAnimation().start();
}
}
The below code is GraphicsEngine's one. As you can see, there is in particular one important variable which is incremented during the circle algorithm : its name is counter_max. Why this variable is important ? Because it's necessary used in the class ImageAnimation. Look at its source after GraphicsEngine's one.
CLASS GraphicEngine
public class GraphicEngine extends Service<Void> {
private int image_width, image_height;
private PixelReader pixel_reader;
private BlockingQueue<Pixel> updates;
private String operation_to_do;
private int radius; [...]
public void setOperationToDo(String operation_to_do) {
this.operation_to_do = operation_to_do;
}
public Task<Void> createTask() {
return new Task<Void>() {
protected Void call() {
switch(operation_to_do) {
[...]
case "Circle" :
traceCircularGradation();
break;
}
return null;
}
};
}
private void traceCircularGradation() {
double w = 2 * 3.141, precision = 0.001;
long counter_max = 0;
int x, y;
this.gui.getImageAnimation().setMax(counter_max);
double[] rgb_gradation;
for (double current_thickness = 0; current_thickness <= this.thickness; current_thickness++) {
for (double angle = 0; angle <= w; angle += precision) {
x = (int) ((current_thickness + radius) * Math.cos(angle) + x0);
y = (int) ((current_thickness + radius) * Math.sin(angle) + y0);
if(x >= 0 && y >= 0) {
counter_max++;
rgb_gradation = PhotoRetouchingFormulas.chromatic_gradation(angle, w);
updates.add(new Pixel(x, y, Color.color(rgb_gradation[0], rgb_gradation[1], rgb_gradation[2])));
}
}
}
this.gui.getImageAnimation().setMax(counter_max);
}
The variable counter_max is used in ImageAnimation (its name has changed, its called : max). It's useful in the last if : if (count >= max).
max/counter_max represent the number of modified pixels. I can't replace it with image_width * image_height because in the circle algorithm, only the circle's pixels are drawn/modified. The other pixels of the image are not.
So I have either to compute counter_max as I did here in the for loops and then give it to ImageAnimation, or find a mathematical formula to determine it before the for. In the first case, the display of the circle doesn't work.
It would be really perfect if a formula existed.
CLASS ImageAnimation
public class ImageAnimation extends AnimationTimer {
private Gui gui;
private long max;
private long count, start;
ImageAnimation (Gui gui) {
this.gui = gui;
this.count = 0;
this.start = -1;
}
public void setMax(long max) {
this.max = max;
}
#Override
public void handle(long timestamp) {
if (start < 0) {
start = timestamp ;
return ;
}
WritableImage writable_image = this.gui.getWritableImage();
BlockingQueue<Pixel> updates = this.gui.getUpdates();
while (timestamp - start > (count* 5_000_000) / (writable_image.getWidth()) && ! updates.isEmpty()) {
Pixel update = updates.remove();
count++;
writable_image.getPixelWriter().setColor(update.getX(), update.getY(), update.getColor());
}
if (count >= max) {
this.count = 0;
this.start = -1;
stop();
}
}
public void startAnimation() {
this.start();
}
}
If you need to compute max later, then you can't initialize it to zero (because if you do any updates before it is set to its "correct" value, then count will exceed max and you will stop the animation). So just initialize it to something it will never reach, e.g. Long.MAX_VALUE.
Relatedly, since you are accessing max from multiple threads, you should either synchronize access to it, or (better) use an AtomicLong. I.e.
public class ImageAnimation extends AnimationTimer {
private Gui gui;
private AtomicLong max;
private long count, start;
ImageAnimation (Gui gui) {
this.gui = gui;
this.count = 0;
this.start = -1;
this.max = new AtomicLong(Long.MAX_VALUE);
}
public void setMax(long max) {
this.max.set(max);
}
#Override
public void handle(long timestamp) {
if (start < 0) {
start = timestamp ;
return ;
}
WritableImage writable_image = this.gui.getWritableImage();
BlockingQueue<Pixel> updates = this.gui.getUpdates();
while (timestamp - start > (count* 5_000_000) / (writable_image.getWidth()) && ! updates.isEmpty()) {
Pixel update = updates.remove();
count++;
writable_image.getPixelWriter().setColor(update.getX(), update.getY(), update.getColor());
}
if (count >= max.get()) {
this.count = 0;
this.start = -1;
this.max.set(Long.MAX_VALUE);
stop();
}
}
public void startAnimation() {
this.start();
}
}
Recently, I've been using Greenfoot, and I wanted to make a simple top-down/bullet-hell shooter. Everything was going smoothly until I tried some of the movement. It's simple enough with good old "arrow keys to move, space to shoot", but the problems show up when I try to move diagonally up-left or down-right and shoot at the same time. I can only do one at a time. I noticed that the directions are related by their location in the else-if calls, but that got me no where. I've also tried moving the code around, replacing the fire() call in act; with the if check entirely, but nothing has changed it.
import greenfoot.*;
/**
* Write a description of class PlayerShip here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class PlayerShip extends SmoothMover
{
private int stepSize = 4;
private boolean tiltLeft = false;
private boolean tiltRight = false;
private int tiltFrame = 1;
private int flameFrame = 0;
private final int COOLDOWN = 20;
private int armsCool = 0;
public PlayerShip()
{
}
/**
* Act - do whatever the PlayerShip wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
public void act()
{
setImage();
if(Greenfoot.isKeyDown("left") && Greenfoot.isKeyDown("right"))
{
tiltFrame = 1;
}
move();
fire();
armsCool ++;
}
public void setImage()
{
if(Greenfoot.isKeyDown("up"))
{
setLocation(getX(), getY() - stepSize - 2);
}
else if(Greenfoot.isKeyDown("down"))
{
setLocation(getX(), getY() + stepSize + 2);
}
if (Greenfoot.isKeyDown("left")) {
setLocation(getX() - stepSize, getY());
tiltLeft = true;
if(tiltFrame == 1)
{
setImage("LeftTilt1.png");
tiltFrame ++;
}
else if(tiltFrame == 2)
{
setImage("LeftTilt2.png");
tiltFrame++;
}
else if(tiltFrame == 3)
{
setImage("LeftTilt3.png");
tiltFrame++;
}
else
{
if(flameFrame == 1)
{
setImage("LeftTilt.png");
flameFrame --;
}
else
{
setImage("LeftTiltAlt.png");
flameFrame ++;
}
}
}
else if (Greenfoot.isKeyDown("right")) {
setLocation(getX() + stepSize,getY());
tiltRight = true;
if(tiltFrame == 1)
{
setImage("RightTilt1.png");
tiltFrame ++;
}
else if(tiltFrame == 2)
{
setImage("RightTilt2.png");
tiltFrame++;
}
else if(tiltFrame == 3)
{
setImage("RightTilt3.png");
tiltFrame++;
}
else
{
if(flameFrame == 1)
{
setImage("RightTilt.png");
flameFrame --;
}
else
{
setImage("RightTiltAlt.png");
flameFrame ++;
}
}
}
else
{
tiltFrame = 1;
tiltLeft = false;
tiltRight = false;
if(flameFrame == 1)
{
setImage("PlayerShip2.png");
flameFrame --;
}
else
{
setImage("PlayerShip.png");
flameFrame ++;
}
}
}
private void fire()
{
if(Greenfoot.isKeyDown("space") && (armsCool >= COOLDOWN))
{
getWorld().addObject(new PlayerBasicBullet(new Vector(12, 5), 251), this.getX(), this.getY());
Battleground.bulletsOnScreen ++;
armsCool = 0;
}
}
}
The move(); method and Vector class are separate and just for smoother movement. I can provide those too, but there shouldn't be anything in there that messes with the controls.
A slightly modified version of the code works fine on my machine. There's two possibilities that I can think of:
Your setImage calls (which I commented out, because I don't have those images) could be throwing an exception, in which case the fire() method wouldn't be reached. Seems unlikely, though, as this would happen even without diagonal movement, and you'd see an exception in the terminal. So, much more likely:
Your keyboard may not be able to register those particular three-key combinations. Three ways to test this:
a. Try this keyboard ghosting demo (click the keyboard at the top of the page) and see if it can register all the keys.
b. load the Asteroids scenario from the book examples and see if you can shoot bullets while accelerating and steering.
c. You could just change "space" in your code to, say, "x" and see if works with that key.
Otherwise, I'm at a loss, unless there is code elsewhere in the scenario causing a problem.