Logic Error in Player Controls in Greenfoot - logic

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.

Related

How do I make timer change a sprite every second without using code that's redundant?

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++

Unity prefab acting weird?

I wrote some code that allows the user to instantiate objects in editor mode by loading the input images in streaming assets and then creating objects using these images.
This has worked well, the problem is when I try to create a prefab with one of these objects. For some reason, the image is not saved in the prefab, and so when I load that prefab, I get white images instead of the ones that were in the original gameobject.
Update: It turns out that my background screen is actually working correctly, but none of the other gameobjects are. So I am not sure what is wrong and why it is only working for some objects but not the other.
here is my code:
public class PlacementObject : MonoBehaviour
{
private loadbackgroundImages backgroundReference;
public Sprite mouseShape;
private Image mouseSprite;
private RectTransform mouseMovement;
public Canvas myCanvas;
private int currentState = 0;
private bool canMove = true;
public List<Texture2D> allButtons;
private List<Sprite> allButtonsSprite;
private List<Sprite> allUISprites;
private List<Texture2D> allUIElements;
private int uiButtonStates = 0;
private int uiElementStates = 0;
public GameObject afterImport;
private GameObject clonedObject;
public GameObject child;
public GameObject objectToBeExported;
private bool isLoading = true;
private bool isfinishedUploading;
private Vector2 defaultSize;
// Use this for initialization
void Start()
{
allUIElements = new List<Texture2D>();
allButtons = new List<Texture2D>();
var info = new DirectoryInfo(Application.streamingAssetsPath + "/" + "UIButtons");
var fileInfo = info.GetFiles();
foreach (FileInfo file in fileInfo)
{
if (file.Extension == ".png" || file.Extension == ".jpg")
{
StartCoroutine(uploadButtonImages(System.IO.Path.Combine("file:///" + Application.streamingAssetsPath, "UIButtons/" + file.Name)));
}
}
var info2 = new DirectoryInfo(Application.streamingAssetsPath + "/" + "UIElements");
var fileInfo2 = info2.GetFiles();
foreach (FileInfo file2 in fileInfo2)
{
if (file2.Extension == ".png" || file2.Extension == ".jpg")
{
StartCoroutine(uploadUiImages(System.IO.Path.Combine("file:///" + Application.streamingAssetsPath, "UIElements/" + file2.Name)));
}
}
allButtonsSprite = new List<Sprite>();
allUISprites = new List<Sprite>();
//createSpritesForButtons(allButtons);
// createSpritesForElements(allUIElements);
mouseSprite = GetComponent<Image>();
mouseSprite.sprite = mouseShape;
mouseMovement = GetComponent<RectTransform>();
backgroundReference = FindObjectOfType<loadbackgroundImages>();
isfinishedUploading = true;
defaultSize = mouseMovement.sizeDelta;
}
// Update is called once per frame
void Update()
{
print(isLoading);
if (isfinishedUploading && backgroundReference.isfinished)
{
isLoading = false;
}
Cursor.visible = false;
transform.position = Input.mousePosition;
Vector2 pos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(myCanvas.transform as RectTransform, Input.mousePosition, myCanvas.worldCamera, out pos);
transform.position = myCanvas.transform.TransformPoint(pos);
if (isLoading == false)
{
if (currentState == 2)
{
mouseSprite.sprite = allUISprites[uiElementStates];
}
if (currentState == 1)
{
mouseSprite.sprite = allButtonsSprite[uiButtonStates];
}
if (Input.GetKeyDown(KeyCode.V))
{
currentState = 0;
mouseSprite.sprite = mouseShape;
mouseMovement.sizeDelta = defaultSize;
}
else if (Input.GetKeyDown(KeyCode.B))
{
currentState = 1;
}
else if (Input.GetKeyDown(KeyCode.N))
{
currentState = 2;
}
if (Input.GetMouseButtonDown(0))
{
placeObject();
}
if (Input.GetAxis("Horizontal") > 0 && canMove)
{
canMove = false;
if (currentState == 0)
{
changeBackgroundNext();
}
else if (currentState == 1)
{
changeButtonNext();
}
else if (currentState == 2)
{
changeElementNext();
}
}
if (Input.GetMouseButton(1))
{
rotateObject();
}
if (Input.GetAxis("Vertical") < 0)
{
if (currentState == 1 || currentState == 2)
{
mouseMovement.sizeDelta -= new Vector2(1, 1);
}
}
if (Input.GetAxis("Vertical") > 0)
{
if (currentState == 1 || currentState == 2)
{
mouseMovement.sizeDelta += new Vector2(1, 1);
}
}
if (Input.GetAxis("Horizontal") < 0 && canMove)
{
canMove = false;
if (currentState == 0)
{
changeBackgroundPrev();
}
else if (currentState == 1)
{
changeButtonPrev();
}
else if (currentState == 2)
{
changeElementPrev();
}
}
if (Input.GetAxis("Horizontal") == 0)
{
canMove = true;
}
if (Input.GetKeyDown(KeyCode.Space))
{
var newBackgroundSprite = backgroundReference.allSprites[backgroundReference.imageIndex];
backgroundReference.imageReference.sprite = newBackgroundSprite;
// exportObject();
// importObject();
#if UNITY_EDITOR
var prefab = PrefabUtility.CreatePrefab( "Assets/Resources/Image.prefab", FindObjectOfType<loadbackgroundImages>().gameObject);
AssetDatabase.Refresh();
#endif
}
}
}
void rotateObject()
{
if (currentState == 1 || currentState == 2)
mouseMovement.eulerAngles += new Vector3(0, 0, 1);
}
private void exportObject()
{
UIData saveData = new UIData();
saveData.inputObject = objectToBeExported;
//Save data from PlayerInfo to a file named players
DataSaver.saveData(saveData, "UI");
}
public void importObject()
{
UIData loadedData = DataSaver.loadData<UIData>("UI");
if (loadedData == null)
{
return;
}
//Display loaded Data
Debug.Log("Life: " + loadedData.inputObject);
afterImport = loadedData.inputObject;
}
private void changeBackgroundNext()
{
backgroundReference.imageIndex++;
if (backgroundReference.imageIndex >= backgroundReference.allSprites.Count)
{
backgroundReference.imageIndex = 0;
}
}
private void changeButtonNext()
{
uiButtonStates++;
if (uiButtonStates >= allButtonsSprite.Count)
{
uiButtonStates = 0;
}
}
private void changeElementNext()
{
uiElementStates++;
if (uiElementStates >= allUISprites.Count)
{
uiElementStates = 0;
}
}
private void changeElementPrev()
{
uiElementStates--;
if (uiElementStates < 0)
{
uiElementStates = allUISprites.Count - 1;
}
}
private void changeButtonPrev()
{
uiButtonStates--;
if (uiButtonStates < 0)
{
uiButtonStates = allButtonsSprite.Count - 1;
}
}
private void changeBackgroundPrev()
{
backgroundReference.imageIndex--;
if (backgroundReference.imageIndex < 0)
{
backgroundReference.imageIndex = backgroundReference.allSprites.Count - 1;
}
}
IEnumerator uploadButtonImages(string url)
{
WWW www = new WWW(url);
yield return www;
if (www != null)
{
allButtons.Add(www.texture);
allButtonsSprite.Add(Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0.5f, 0.5f)));
}
}
private void createSpritesForButtons(List<Texture2D> allTextures)
{
for (int i = 0; i < allTextures.Count; i++)
{
Sprite tempSprite = Sprite.Create(allTextures[i], new Rect(0, 0, allTextures[i].width, allTextures[i].height), new Vector2(0.5f, 0.5f));
tempSprite.name = "Button" + i;
allButtonsSprite.Add(tempSprite);
}
}
IEnumerator uploadUiImages(string url)
{
WWW www = new WWW(url);
yield return www;
print(url);
if (www != null)
{
allUIElements.Add(www.texture);
allUISprites.Add(Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0.5f, 0.5f)));
}
}
private void createSpritesForElements(List<Texture2D> allTextures)
{
for (int i = 0; i < allTextures.Count; i++)
{
Sprite tempSprite = Sprite.Create(allTextures[i], new Rect(0, 0, allTextures[i].width, allTextures[i].height), new Vector2(0.5f, 0.5f));
tempSprite.name = "" + i;
allUISprites.Add(tempSprite);
}
}
private void placeObject()
{
if (currentState == 1)
{
var gameObject = Instantiate(child, transform.position, Quaternion.Euler(mouseMovement.eulerAngles.x, mouseMovement.eulerAngles.y, mouseMovement.eulerAngles.z), backgroundReference.transform);
gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(mouseMovement.sizeDelta.x, mouseMovement.sizeDelta.y);
var newSprite = allButtonsSprite[uiButtonStates];
gameObject.GetComponent<Image>().sprite = newSprite;
gameObject.AddComponent<Button>();
}
if (currentState == 2)
{
var gameObject = Instantiate(child, transform.position, Quaternion.Euler(mouseMovement.eulerAngles.x, mouseMovement.eulerAngles.y, mouseMovement.eulerAngles.z), backgroundReference.transform);
gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(mouseMovement.sizeDelta.x, mouseMovement.sizeDelta.y);
var newSprite = allUISprites[uiElementStates];
gameObject.GetComponent<Image>().sprite = newSprite;
}
}
}
When you're changing some object's state via an inspector script you have to tell Unity, that you've done some changes, otherwise it doesn't know that something has happened to objects at the scene. And according to the information about changes on your scene (also when you change it manually, not by script) Unity serializes all data on your scene to save all the information. And when you load scene (doesn't matter in editor or in play mode) Unity deserializes all the objects on scene to show you. As far as serializing/deserializng object is rather expensive operation Unity only does it when it's really necessary - in case of saving scene/object it's only performed when it knows that there're some changes that needs to be saved.
The actual solution of your problem depends on Unity version you're using. Try reading this post. Such things as Undo and MarkAllScenesDirty should help you. For older versions of Unity EditorUtility.SetDirty should help.
So as people pointed out like Vmchar, when you save the prefab using code. it gets saved using the initial state without any of the stuff that got loaded to it during runtime.
I found out a solution for this though, all you need to do is make a script that would save the image by serializing it in streamingassets (plenty of tutorials online on how to serialize image), and then loading it using www stream when needed in the other scene.

How can I display pixel by pixel the calculation of a circle?

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();
}
}

Object wont detect edge of world in greenfoot

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.

Telerik RadPageview Right-to-Left Layout Arrow Keys

I have a RadPageview in Strip Mode and I want my program to had support a right-to-left language. When I changing the Right-to-Left Property to true it works fine. but when I run my program and use Arrow Keys (Left or Right) it doesn't work correctly. Left key goes to Right and Right key goes to Left. How can I fix it?
Telerik answered my question:
http://www.telerik.com/forums/right-to-left-arrow-keys-problem
the possible solution that I can suggest is to create a custom RadPageViewStripElement and override its IsNextKey and IsPreviousKey methods on a way to reverse the previous/next navigation as follows:
public class CustomPageView : RadPageView
{
public override string ThemeClassName
{
get
{
return typeof(RadPageView).FullName;
}
}
protected override RadPageViewElement CreateUI()
{
switch (this.ViewMode)
{
case PageViewMode.Strip:
return new CustomRadPageViewStripElement();
default:
return base.CreateUI();
}
}
}
public class CustomRadPageViewStripElement : RadPageViewStripElement
{
public CustomRadPageViewStripElement()
{
}
protected override Type ThemeEffectiveType
{
get
{
return typeof(RadPageViewStripElement);
}
}
protected override bool IsNextKey(Keys key)
{
if (this.RightToLeft)
{
if (key == Keys.Left)
{
return true;
}
else
{
return false;
}
}
return base.IsNextKey(key);
}
protected override bool IsPreviousKey(Keys key)
{
if (this.RightToLeft)
{
if (key == Keys.Right)
{
return true;
}
else
{
return false;
}
}
return base.IsPreviousKey(key);
}
}
I fix it programmatically by using ProcessCmdKey of form.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Right))
{
int x = radPageView1.Pages.IndexOf(this.radPageView1.SelectedPage) - 1;
if (x < 0)
x = radPageView1.Pages.Count() - 1;
else if (x >= radPageView1.Pages.Count())
x = 0;
radPageView1.SelectedPage = radPageView1.Pages[x];
return true;
}
else if(keyData == Keys.Left)
{
int x = radPageView1.Pages.IndexOf(this.radPageView1.SelectedPage) + 1;
if (x <= 0)
x = radPageView1.Pages.Count() - 1;
else if (x >= radPageView1.Pages.Count())
x = 0;
radPageView1.SelectedPage = radPageView1.Pages[x];
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}

Resources