I am using libGdx to code a Pong-like game.
In the GameScreen I want to display the remaining lives of each player.
For that, in the create() of the GameScreen I have a table containing several ImageButtons:
for(int i = 0; i < lifePlayer1; i++){
lives[i] = new ImageButton(skin, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
Each ImageButton represent a life. When a player loses a life, the ImageButton corresponding to the lost life is setChecked, and the imageChecked represents the lost life
Before the game start I want to display a message saying "Game Starts !". The message crosses the screen from left to right, and the game starts only when the message has disappeared.
The problem I encounter is that the instantiation of the lifeTable takes time, and the GameScreen starts running before the instantiation is finished. I explain :
When I disable the lifeTable, everything runs smoothly, the "Game Starts !" message crosses the screen from left to right and the game starts.
When I enable the lifeTable, when I press on the "New Game" button in my MainMenueScreen, there is a delay (let's say 0.5 seconds), like a loading time, and when the GameScreen is finally displayed, the "Game Starts !" message has almost finished crossing the screen.
I have a 2 players mode, with 2 lifeTable to load, and it's even worse, when the GameScreen displays, in 2 players mode, the "Game Starts !" message has already finished crossing the screen and the game has already started.
I am using an AssetManager, and every texture used in the game are already loaded before reaching the MainMenuScreen. The AssetManager helped to reduce the GameScreen loading time, but it's still not satisfying.
Is there something to preload the lifeTable, the same way we preload the textures with the AssetManager ?
Or, is there a way to have the render() of the GameScreen starts only when the create() has finished the instantiation of ever objects ?
Thank you for your help.
is probably not the most efficient way to make but which occurred to me while reading your question, and it is simple
Variable Class
private boolean loadForTest = false;
.
#Override
public void show() {
.//Other Code
image = new Image(YourAssetManager.get("YourImage.png", Texture.class));
image.addAction(Actions.sequence(
Actions.moveTo(Gdx.graphics.getWidth(),
(Gdx.graphics.getHeight()/2),
1f)));
image.setPosition((-100f), Gdx.graphics.getHeight()/2);
YourStage.addActor(image);
}
#Override
public void render(float delta) {
.//Other Code
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
if (loadForTest == false){
if (image.getActions().size == 0){
Gdx.app.log("test" ,"now load table");
for (int i = 0; i <lifePlayer1; i ++) {
vidas [i] = new ImageButton (piel, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
loadForTest = true;
}else{
Gdx.app.log("test" ,"Your Other Code Play");
}
}
}
Edit
added another code because my English is not very good and did not know very well what you wanted to do
Variable Class
private boolean loadForTest = false;
.
#Override
public void render(float delta) {
.//Other Code
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
if (loadForTest == false){
for (int i = 0; i <lifePlayer1; i ++) {
vidas [i] = new ImageButton (piel, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
loadForTest = true;
}else{
Gdx.app.log("test" ,"Your Image load, Your Other Code Play");
}
}
Related
I have been using TMP objects in several instances in my game, but all of a sudden it decides not to work on a certain object.
public class BeforeRoundTimer : MonoBehaviour
{
public TextMeshProUGUI timer;
private Timer oneSecondTimer;
private int time = 5;
public void StartCountdown()
{
Debug.Log("One second timer");
oneSecondTimer = new Timer(1000);
oneSecondTimer.Elapsed += UpdateTime;
oneSecondTimer.Enabled = true;
oneSecondTimer.AutoReset = true;
oneSecondTimer.Start();
}
private void UpdateTime(object source, ElapsedEventArgs e)
{
if(time == 0)
{
oneSecondTimer.Stop();
return;
}
timer.text = $"{time}";
time--;
}
}
I know the text is updating because I put debug statements (I have since removed them) and they fired when UpdateTime() is called. I also viewed the inspector when the game was playing, and the text value would update in front of my eyes. The text only changes when I make some stylistic change to it (i.e. making it bold, changing the font asset, including changing the text itself). I have looked back to my old code and it basically runs the exact same way, but it actually changes in game.
Ok so after taking a break, I decided to find another way to call my method every second. Instead of using a Timer, I decided to use Unity's InvokeRepeating() function.
public class BeforeRoundTimer : MonoBehaviour
{
public TextMeshProUGUI timer;
private int count = 0;
public void StartCountdown()
{
InvokeRepeating(nameof(UpdateTime), 0, 1f);
}
private void UpdateTime()
{
if(count == 5)
{
CancelInvoke("UpdateTime");
return;
}
Debug.Log("Update Time");
timer.text = $"{5 - count}";
count++;
}
}
One thing I noticed when trying to use the Timer in a different way is that it was only updating the text value every other second. It ran 10 times (I put a Debug.Log() in UpdateTime()) but only changed the value every other time while not actually updating the TMP. You could replace nameof(UpdateTime) with "UpdateTime", but Visual Studio recommended that I use the former so I went with that.
In short: don't use timers, use Unity's InvokeRepeating() function because it works perfectly. It is actually very similar to JavaScript's setInterval() which I found interesting.
I'm trying to instantiate a UI image in unity 5 to display a players health. I've used a for loop to spawn a new health block image for every 10 health the player has remaining. However, I can't get the image to actually show on screen (it works when I set up the prefab, but as soon as I try to instantiate it through the code it vanishes). The code isn't finished yet as at the moment I understand all the images will stack on top of each other rather than in a row (which will be the finished result) but I want to make sure I can get the images to actually show first before taking this further. Any help would be appreciated!
Things to know:
Player health is set to 100,
Health per icon is set to 10,
I'm using a prefab (health that only contains a UI image component within my canvas.
code:
using UnityEngine.UI;
public class GameController : MonoBehaviour {
public GameObject player;
public int playerStartingHealth;
public Image healthBarGreen;
public int healthPerIcon;
void Start () {
score = 0;
UpdateScore ();
AddPlayerHealth (playerStartingHealth / healthPerIcon);
StartCoroutine (spawnWaves ());
}
public void AddPlayerHealth (int n) {
for (int i =0; i < n; i++) {
Instantiate(healthBarGreen.gameObject);
}
}
}
Bit of a necro, but this post is high on a google search on the subject. Solution:
Image newImage = Instantiate(prefabImage, Vector3.zero, Quaternion.identity);
newImage.transform.SetParent(canvas.transform, false);
newImage.rectTransform.anchoredPosition = Vector3.zero; // your new position here
the code below is for a fundraiser dinner to purchase a land, the purpose is to show the progress of the square meter of land purchased (around 2976m2). everytime a square meter is purchased, the application adds an image tile which corresponds to an acctual 1m2. eventually the tiles (~2976 of them) fill up like in a grid to complete the land once fully purchased.
The size of each tiles is around 320bytes, there are 2976 tiles in total.
I have also showing below an image example.
The thing that drives me crazy with this code (in javafx) is that it consumes around 90 to 100% of 1 of my processors and the memory usage keeps increasing as the tiles add up until the code buffer run out of memory and the program crashes after a while. this is not desirable during the fundraising dinner.
the full code is available for testing at
you will need to change boolean split to true false, which will split the images for you, (around 3000 images);
https://github.com/rihani/Condel-Park-Fundraiser/tree/master/src/javafxapplication3
The main culprit that uses all the memory and CPU is the AnimationTimer() function shown below and I am wondering if anyone can help me reduce memory and CPU usage in this code.
to briefly explain how the code below is used, the land is divided into 2 panes, when the first one grid_pane1 is filled up the second pane grid_pane2 starts to then fill up.
also a flashing tile is used to show the current progress.
I am using total_donnation ++; to test the code, but would normally use mysql to pull the new value raised during the findraising dinner
AnimationTimer() Code:
translate_timer = new AnimationTimer() {
#Override public void handle(long now) {
if (now > translate_lastTimerCall + 10000_000_000l)
{
old_total_donnation = total_donnation;
try
{
// c = DBConnect.connect();
// SQL = "Select * from donations";
// rs = c.createStatement().executeQuery(SQL);
// while (rs.next())
// {total_donnation = rs.getInt("total_donnation");}
// c.close();
total_donnation ++;
if(total_donnation != old_total_donnation)
{
System.out.format("Total Donation: %s \n", total_donnation);
old_total_donnation = total_donnation;
if (!pane1_full)
{
grid_pane1.getChildren().clear();
grid_pane1.getChildren().removeAll(imageview_tile1,hBox_outter_last);
}
grid_pane2.getChildren().clear();
grid_pane2.getChildren().removeAll(imageview_tile2,hBox_outter_last);
for(i=0; i<=total_donnation; i++)
{
if (pane1_full){ System.out.println("Pane 1 has not been redrawn"); break;}
file1 = new File("pane1_img"+i+".png");
pane1_tiled_image = new Image(file1.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile1 = new ImageView(pane1_tiled_image);
grid_pane1.add(imageview_tile1, current_column_pane1,current_row_pane1);
current_column_pane1 = current_column_pane1+1;
if (current_column_pane1 == max_columns_pane1 )
{
current_row_pane1 = current_row_pane1+1;
current_column_pane1 = 0;
}
if (i == max_donnation_pane1 ){ pane1_full = true; System.out.println("Pane 1 full"); break;}
if (i == total_donnation)
{
if (i != max_donnation_pane1)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft1 = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft1.setFromValue(1.0);
ft1.setToValue(0.3);
ft1.setCycleCount(Animation.INDEFINITE);
ft1.setAutoReverse(true);
ft1.play();
grid_pane1.add(hBox_outter_last, current_column_pane1,current_row_pane1);
}
}
}
if (i < total_donnation)
{
total_donnation_left = total_donnation - max_donnation_pane1;
for(j=0; j<=total_donnation_left; j++)
{
file2 = new File("pane2_img"+j+".png");
pane2_tiled_image = new Image(file2.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile2 = new ImageView(pane2_tiled_image);
grid_pane2.add(imageview_tile2, current_column_pane2,current_row_pane2);
current_column_pane2 = current_column_pane2+1;
if (current_column_pane2 == max_columns_pane2 )
{
current_row_pane2 = current_row_pane2+1;
current_column_pane2 = 0;
}
if (j == max_donnation_pane2 ){ System.out.println("Pane 2 full"); break;}
if (j == total_donnation_left)
{
if (j != max_donnation_pane2)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft.setFromValue(1.0);
ft.setToValue(0.3);
ft.setCycleCount(Animation.INDEFINITE);
ft.setAutoReverse(true);
ft.play();
grid_pane2.add(hBox_outter_last, current_column_pane2,current_row_pane2);
}
}
}
}
current_column_pane1 =0;
current_row_pane1=0;
current_column_pane2=0;
current_row_pane2=0;
}
}
catch (Exception ex) {}
translate_lastTimerCall = now;
}
}
};
First and foremost, you create a lot of indefinite FadeTransitions that are never stopped. These add up over time and cause both memory and CPU leaks. You should stop() the transition before starting a new one. Alternatively, you only need one transition to interpolate the value of a DoubleProperty and then bind node's opacity to this property:
DoubleProperty opacity = new SimpleDoubleProperty();
Transition opacityTransition = new Transition() {
protected void interpolate(double frac) {
opacity.set(frac);
}
};
// elsewhere
hBox_outter_last.opacityProperty().bind(opacity);
You may want to preload all the image tiles beforehand, so that you avoid reading from disk in the loop.
You unnecessarily destroy and recreate large part of the scene in every cycle. You should modify your code to only add the new tiles and not drop them all and recreate them from scratch.
Finally, when you actually query the database, you should do it from a different thread and not the JavaFX application thread, because your UI will be unresponsive for the time of the query (e.g. not animating your fade transitions).
I have a suggestion:
Do not split the image instead using 2 panels. One for displaying the whole image. The second will be a grid pane overlapping the first pane. Therefore, when a square meter is purchased, the background of corresponding grid-cell will become transparent.
I have a 3dtext named Play, which when clicked will play the animation; the other one is named Back, which reverses the animation. Problem is after I Played and Backed it, the animation wont play anymore when i clicked Play.
The animation named redsubmenu is in legacy and clamp forever wrap mode.
public class PlayButtonScript : MonoBehaviour {
//public static PlayButtonScript pbs;
public GameObject redsubmenu;
void Update(){
#if UNITY_EDITOR
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Input.GetMouseButtonDown(0)&&Physics.Raycast(ray,out hit)){
if(hit.collider.name == "Play"){
redsubmenu.animation.Play();
}
}
#endif
}
}
public class BackButtonScript : MonoBehaviour {
// Update is called once per frame
void Update () {
#if UNITY_EDITOR
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Input.GetMouseButtonDown(0)&&Physics.Raycast(ray, out hit)){
if(hit.collider.name == "Back"){
transform.parent.animation["redsubmenu"].speed = -1;
transform.parent.animation.Play("redsubmenu");
}
}
#endif
}
}
It appears that you never reset the speed of the animation back to 1. When you click play the first time the speed is initially 1, so it works fine. However, when you back you set the speed to -1 and it is never set to any other value.
Try using:
if (hit.collider.name == "Play") {
transform.parent.animation["redsubmenu"].speed = 1;
redsubmenu.animation.Play();
}
in your play button script.
You might also be able to make use of Animation.Rewind.
http://docs.unity3d.com/ScriptReference/Animation.Rewind.html
Just to be more specific, i edited my playbuttonscript as shown below:
if(Input.GetMouseButtonDown(0)&&Physics.Raycast(ray,out hit)){
if(hit.collider.name == "Play"){
if(redsubmenu.animation["redsubmenu"].speed == -1){
redsubmenu.animation["redsubmenu"].speed = 1;
} else {
redsubmenu.animation.Play();
}
}
}
in my back button, i delete the transform.parent.animation.Play, no need for that.
I am new to XNA and CSharp programming so I want to learn to make a treasure hunting game as a beginning so I made a player(as a class) which can walk up, down, left and right. I made a Gem class also which the player can collide with and the gem disappears and a sound is played. But I want to make some walls that the player can collide with and stop so I made a class called Tile.cs (The wall class) and I made a void in it
public void CollideCheck(bool tWalk, bool bottomWalk, bool leftWalk, bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect)
{
colRect = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
if (this.colRect.Intersects(topRect))
{
tWalk = false;
}
else
tWalk = true;
if (this.colRect.Intersects(bottomRect))
{
bottomWalk = false;
}
else
bottomWalk = true;
if (this.colRect.Intersects(leftRect))
{
leftWalk = false;
}
else
leftWalk = true;
if (this.colRect.Intersects(rightRect))
{
rightWalk = false;
}
else
rightWalk = true;
}
Then, in the Game1.cs (The main Class) I made an array of "Tiles":
Tile[] tiles = new Tile[5];
And in the update void I made this:
foreach (Tile tile in tiles)
{
tile.CollideCheck(player.topWalk, player.bottomWalk, player.leftWalk, player.rightWalk,
new Rectangle((int)player.Position.X, (int)player.Position.Y - (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X, (int)player.Position.Y + (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X + (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X - (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight));
}
All those rectangles are the borders of the player but when I run the game the player doesn't collide with it so is there any way to fix this?
I can post the project if I am not very clear.
Your parameters are in only, but you set their values inside the call. You have to declare them as out variables so that their value is sent back to the caller. Using out also makes sure you always set a value to them before exiting the function.
So change your function declaration to public void CollideCheck(out bool tWalk, out bool bottomWalk, out bool leftWalk, out bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect) and you get the values back.