Player life is increasing per frame instead of per second - c#

I am making a game and I added lives and score in it. Everything is ok but something I want to add is that if the score is multiple of 15 then lives increase by 1.
The problem that I am having here is that the lives are increasing per frame and not per second. 60 frames per second, so it is increasing lives by 60 instead of 1.
The part that is working it is in 'update'
public class RobotDodge
{
private Player _Player;
private Window _GameWindow;
private List<Robot> _Robots;
//private int _Lives;
private decimal _Score, _Lives;
private DateTime _Time, currentTime;
public bool Quit{
get{
return _Player.Quit;
}
}
// making our game constructor..
public RobotDodge(){
_GameWindow=new Window("Robot Dodge", 800, 800);
_Player=new Player(_GameWindow);
_Robots = new List<Robot>();
_Lives = _Player._Lives; // player lives are 5.
_Score = 0;
//_Counter = 0;
currentTime = DateTime.Now;
_Time = DateTime.Now;
}
// method that returns our new Robot object..
public Robot RandomRobot(){
float r = SplashKit.Rnd();
Boxy _Boxy=new Boxy(_GameWindow, _Player);
Roundy _Roundy=new Roundy(_GameWindow, _Player);
Boxy_v2 _Boxy_v2=new Boxy_v2(_GameWindow, _Player);
// setting up the probability to return the robot type..
if(r < 0.3){
return _Boxy;
}else if(r > 0.3 && r < 0.6){
return _Roundy;
}else{
return _Boxy_v2;
}
}
public void DrawGame(){
// clearing the window..
_GameWindow.Clear(Color.White);
//draw our robots..
for (int i = 0; i < _Robots.Count; i++){
_Robots[i].Draw();
SplashKit.DrawCircle(Color.Blue, _Robots[i].CollisionCircle);
}
// drawing our player..
_Player.DrawPlayer();
// Number of Lives..
SplashKit.DrawText($"Lives : {_Lives}", Color.Black, "Ariel", 14, 20, 20);
SplashKit.DrawText($"Score : {_Score}", Color.Black, "Ariel", 14, 20, 50);
_GameWindow.Refresh(60);
}
// update method..
public void Update(){
for (int i = 0; i < _Robots.Count; i++){
_Robots[i].UpdateRobot();
}
if (SplashKit.Rnd() < 0.02){
_Robots.Add(RandomRobot());
}
CheckCollisions();
// increase _Time by 1 second..
_Time = _Time.AddSeconds(1);
// increase score by 1 every second..
// the below condition will always be true as the difference between times is always 1 second..
if (_Time.Second - currentTime.Second == 1){
_Score += 1;
}
if (_Score % 15 == 0){
_Lives += 1;
}
}
}

This should resolve. I had to add an update for the current time variable at the top of the loop. I was under the impression you were updating that somewhere else in the code aside from your Robot constructor and I did not want to skew your activity. So add to the first line within the update method.
protected DateTime currentTime = DateTime.Now;
// default to a minimum value
private DateTime _lastTime = DateTime.MinValue;
public void Update()
{
// UPDATE THE TIME HERE based on NOW
currentTime = DateTime.Now;
// just FYI, shortcut readability to loop vs indexing loop.
// An array is an enumerable you can loop through directly
foreach (var oneRobot in _Robots)
oneRobot.UpdateRobot();
// vs what you had
// for (int i = 0; i < _Robots.Count; i++)
//{
// _Robots[i].UpdateRobot();
//}
if (SplashKit.Rnd() < 0.02)
{
_Robots.Add(RandomRobot());
}
CheckCollisions();
// if the first time ever in, just set the last time to whatever current IS and get out.
// you would never increase as score or lives on the first instance as it is basically 0
if (_lastTime == DateTime.MinValue)
{
_lastTime = currentTime;
return;
}
// subtracting one date/time
// creates a "TimeSpan" object which represents the difference between two date/time fields.
// if no full second has completed yet, get out.
if ((currentTime - _lastTime).Seconds < 0)
return;
// a second HAS completed since the last measurement. Update Score and Lives
_Score++;
if (_Score % 15 == 0)
_Lives++;
// NOW, you can adjust, but if its a 1.3 second between cycles, you would be constantly
// getting farther and farther from actual seconds. So what I am proposing is to
// take the last second and just add an absolute one second to it, so when the next
// real Second cycle is complete, it should properly hit again.
_lastTime = _lastTime.AddSeconds(1);
}

Related

How can I make the text to show all the 8 digit numbers and clear the text after that?

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SecurityKeypadSystem : MonoBehaviour
{
[Header("References")]
// rather let this class control the display text
[SerializeField] private TextMesh _text;
[Header("Settings")]
// also rather let this class control the length of a code
[SerializeField] private int _codeLength = 8;
[Header("Debugging")]
[SerializeField] private GameObject[] _keyPadNumbers;
[SerializeField] private List<int> _code = new List<int>();
// This will be invoked once the code length has reached the target length
public event Action<int> OnCodeComplete;
// Start is called before the first frame update
private void Start()
{
var KeyPadNumbersParent = GameObject.FindGameObjectWithTag("KeypadParent").GetComponentsInChildren<Transform>(true);
foreach (Transform child in KeyPadNumbersParent)
{
if (child.gameObject.GetComponent<SecurityKeypadKeys>() != null)
{
var securityKeypadKeys = child.gameObject.GetComponent<SecurityKeypadKeys>();
securityKeypadKeys.onKeyPressed -= HandleKeyPressed;
securityKeypadKeys.onKeyPressed += HandleKeyPressed;
}
}
}
private void OnDestroy()
{
// just for completeness you should always remove callbacks as soon as they are not needed anymore
// in order to avoid any exceptions
foreach (var keyPadNumber in _keyPadNumbers)
{
var securityKeypadKeys = keyPadNumber.GetComponent<SecurityKeypadKeys>();
securityKeypadKeys.onKeyPressed -= HandleKeyPressed;
}
}
// this is called when a keypad key was pressed
private void HandleKeyPressed(int value)
{
// add the value to the list
_code.Add(value);
_text.text += value.ToString();
// Check if the code has reached the target length
// if not do nothing
if (_code.Count <= _codeLength) return;
// if it reached the length combine all numbers into one int
var exponent = _code.Count;
float finalCode = 0;
foreach (var digit in _code)
{
finalCode =digit * Mathf.Pow(10, exponent);
exponent--;
}
// invoke the callback event
OnCodeComplete?.Invoke((int)finalCode);
// and reset the code
ResetCode();
}
// Maybe you later want an option to clear the code field from the outside as well
public void ResetCode()
{
_code.Clear();
_text.text = "";
}
// also clear the input if this gets disabled
private void OnDisable()
{
ResetCode();
}
}
The code length is 8 and this line was only < and I changed it to <=
if (_code.Count <= _codeLength) return;
So now if I type for example 12345678 it will show the 8 digits but to make it clear I need to type another 9 digit and it will not show the 9 digit but I must press on another 9 digit to make it clear.
I want that if I type for example 12345678 or 22222222 show all the eight numbers then clear.
Changing from < to <= show the eight digits but to make it clear I need to add/press on nine digit.
And even not to reset at all let's say I'm not calling the ResetCode method, just make it that if I type show the eight numbers 12345678 and don't the option to keep add and show numbers. The code length is 8 it can be 2 or 100 but it should show the code length.
How about this:
private void HandleKeyPressed(int value)
{
// add the value to the list
_code.Add(value);
_text.text += value.ToString();
// Check if the code has reached the target length
// if yes prcoess further
if (_code.Count == _codeLength)
{
// if it reached the length combine all numbers into one int
var exponent = _code.Count;
float finalCode = 0;
foreach (var digit in _code)
{
finalCode =digit * Mathf.Pow(10, exponent);
exponent--;
}
// invoke the callback event
OnCodeComplete?.Invoke((int)finalCode);
// and reset the code
ResetCode();
}
}

Problems with my invincibility frames feature

I've been working on a game as a project and I've gotten round to introducing invincibility frames when the player takes a heart of damage. In this case I want it so that the player model flashes roughly once every 0.1 seconds and to have the invincibility last for 2 seconds.
I've written this code and I can't figure out why it isn't working. By the way using this code when the player takes damage they cannot take damage afterwards so something is really messed up (it isn't just the visual invincibility being an issue).
(Thank you)
private void loseHealth()
{
if (invinTimerCounter == 0)
{
curHealth -= 1;
invinTimerCounter = invinTimer;
invincibilityBlink();
}
}
private void invincibilityBlink()
{
for (int i = 0; i < 10; i++)
{
Invoke("spriteDisable", 1);
Invoke("spriteEnable", 1);
}
}
private void spriteEnable()
{
this.spriteRenderer.enabled = true;
}
private void spriteDisable()
{
this.spriteRenderer.enabled = false;
}
private void Update()
{
if (invinTimerCounter < 0)
{
invinTimerCounter -= Time.deltaTime;
}
}
In addition to jmalenfant's comment, I'd like to rewrite your invincibilityBlink() method to use a coroutine:
private IEnumerator invincibilityBlink()
{
for (int i = 0; i < 10; i++)
{
spriteDisable();
yield return new WaitForSeconds(0.1f);
spriteEnable();
yield return new WaitForSeconds(0.1f);
}
invincible = false;
}
Then, here:
if (!invincible)
{
curHealth -= 1;
invincible = true;
StartCoroutine(invincibilityBlink());
}
Oh, and we'll change that messy float to a boolean and let the coroutine handle it too, so if you decide to change the invincibility time, you only need to change things in one place.

How to change a picturebox once a gif ends

I have a PictueBox and I have some dice, I would like to play an animation for the "rolling" of the dice, I did a .gif with the dice, but after the dice stop rolling, I want the actual dice number that I got, I have a random funcion that handles that.
My question is, I press the "Roll Dice" button, it plays the animation and after the animation ends I should set int the picturebox the dice that actually came. but it immediately chnages to the dice number that actually came, skipping the animation;
This is how it works:
dice1.Image = Resources.DiceAnimation; //Here the gif is called to be played
int x = rollDice(); //Here I roll the dice
switch (x){
case 1: dice.Image = resources.diceFace1; //Image set depending on x
break
case 2: //etc...
}
There might be two things needed to do that.
Firstly, you may need to ensure that your PictureBox receives a gif image and it knows it. To do this, please check this answer and this answer. The posts have code to show GifImage frame by frame:
public class GifImage
{
private Image gifImage;
private FrameDimension dimension;
private int frameCount;
private int currentFrame = -1;
private bool reverse;
private int step = 1;
public GifImage(string path)
{
gifImage = Image.FromFile(path);
//initialize
dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
//gets the GUID
//total frames in the animation
frameCount = gifImage.GetFrameCount(dimension);
}
public bool ReverseAtEnd {
//whether the gif should play backwards when it reaches the end
get { return reverse; }
set { reverse = value; }
}
public Image GetNextFrame()
{
currentFrame += step;
//if the animation reaches a boundary...
if (currentFrame >= frameCount || currentFrame < 1) {
if (reverse) {
step *= -1;
//...reverse the count
//apply it
currentFrame += step;
}
else {
currentFrame = 0;
//...or start over
}
}
return GetFrame(currentFrame);
}
public Image GetFrame(int index)
{
gifImage.SelectActiveFrame(dimension, index);
//find the frame
return (Image)gifImage.Clone();
//return a copy of it
}
}
Use it like this (note that you need a Timer object):
private GifImage gifImage = null;
private string filePath = #"C:\Users\Jeremy\Desktop\ExampleAnimation.gif";
public Form1()
{
InitializeComponent();
//a) Normal way
//pictureBox1.Image = Image.FromFile(filePath);
//b) We control the animation
gifImage = new GifImage(filePath);
gifImage.ReverseAtEnd = false; //dont reverse at end
}
private void button1_Click(object sender, EventArgs e)
{
//Start the time/animation
timer1.Enabled = true;
}
//The event that is animating the Frames
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image = gifImage.GetNextFrame();
}
Secondly, to know how long you want to run your GIF image, you may need to Get Frame Duration of GIF image like this:
double delayIn10Ms; //declare somewhere
//Initialize on your form load
PropertyItem item = img.GetPropertyItem (0x5100); // FrameDelay in libgdiplus
// Time is in 1/100th of a second
delayIn10Ms = (item.Value [0] + item.Value [1] * 256) * 10;
Then use the delayIn10Ms time plus, probably, a little bit more time to stop your timer. You may also want to check when was the last time your timer Ticks and store it. If it exceeds the given delay time, then you should stop your timer and start it again on dice roll, after image assignment in your switch case.
DateTime currentTick = DateTime.Min;
DateTime startTick = DateTime.Min;
private void timer1_Tick(object sender, EventArgs e)
{
currentTick = DateTime.Now;
if ((currentTick - startTick).TotalSeconds / 100 < delayIn10Ms)
pictureBox1.Image = gifImage.GetNextFrame();
else
timer1.Stop(); //stop the timer
}
//And somewhere else you have
timer1.Start(); //to start the timer
int x = rollDice(); //Here I roll the dice
switch (x){
case 1: dice.Image = resources.diceFace1; //Image set depending on x
break
case 2: //etc...
}
You can make a timer with the Interval property set to the length of the animation and set it's Tag to 0 and in the timer write the code:
if(timer.Tag == "0")
timer.Tag == "1";
else if(timer.Tag == "1")
{
int x = rollDice();
switch (x)
{
case 1: dice.Image = resources.diceFace1; break;
case 2: //etc...
}
timer.Tag == "0";
timer.Stop();
}

Is that 60 frames per second?

public PbsWheel(AnimatedPictureBox.AnimatedPictureBoxs[] pbs, AnimatedPictureBox.AnimatedPictureBoxs pb, int delta,Label label2)
{
for (int i = 0; i < pbs.Length; i++)
{
if (delta > 0)
{
pbs[i].AnimateRate += 1/60 * 1000;
1/60 * 1000 is 60 frames per second ?
This is how i animate the pictureBoxes the images inside. Im using timer for each picturebox:
public class AnimatedPictureBoxs : PictureBox
{
public static bool images;
List<string> imageFilenames;
Timer t = new Timer();
public AnimatedPictureBoxs()
{
images = false;
AnimateRate = 100; //It's up to you, the smaller, the faster.
t.Tick += Tick_Animate;
}
public int AnimateRate
{
get { return t.Interval; }
set { t.Interval = value; }
}
public void Animate(List<string> imageFilenames)
{
this.imageFilenames = imageFilenames;
t.Start();
}
public void StopAnimate()
{
t.Stop();
i = 0;
}
int i;
private void Tick_Animate(object sender, EventArgs e)
{
if (images == true)
{
imageFilenames = null;
}
if (imageFilenames == null)
{
return;
}
else
{
try
{
if (i >= imageFilenames.Count)
{
i = 0;
}
else
{
Load(imageFilenames[i]);
i = (i + 1) % imageFilenames.Count;
}
}
catch (Exception err)
{
}
}
}
The rate is set to 100 what i want to do is to display and when i move the mouse wheel up down to change the speed of the images animate by frames per second.
pbs is array of pictureBoxes.
pbs[i].AnimateRate += 1/60 * 1000;
Now, AnimateRate is an integer property. It is very badly named. It is not a rate. It is a timer interval. In mathematical terms it is a period. Naming it rate makes it sound as though it will be a rate, or a frequency.
The mathematical relationship between period T and frequency f is:
T = 1/f
So, here's what you should do:
Rename the property as AnimationInterval.
When you need to convert a frequency (i.e. frame rate) to an interval use the formula above.
Note that you need to account for the fact that your frequencies are measured in frames per second, but your intervals are measured in milli-seconds. So your code should be:
pbs[i].AnimationInterval += 1000/60;
That looks very similar to what you had but there is a subtle difference. In mathematics, the formulae are identical. But in C#, the behaviour of the / operator depends on the types of its operands. You supply two integers and so / is integer division. And the result of 1/60 is zero. So your code does not modify the property.
I do think that you will need to modify your logic a little. As it stands, your raw data is an interval. But actually what you wish to control if frame rate. So I believe that you should maintain a variable that holds the frame rate. If you want to modify it, then make the modifications to the frame rate variable. And then set the interval like this:
pbs[i].AnimationInterval = 1000/frameRate;

Timer in XNA framework

I have a problem with my game. It is divided in a two states: GameState.MainMenu and GameState.Playing.
I want to draw on screen the timer when I playing. I use gameTime.TotalGameTime.Minutesand
gameTime.TotalGameTime.Seconds variable. But when I clicked on button Play Now, and my game switched state from GameState.MainMenu to GameState.Playing., the timer dosent start from 0. It starts with the time, which elapsed when I spend in MainMenu. I try create next variable to count time, which I spent in MainMenu, and I try to subtract from the first variable, but the displaying time is not properly.
My substracting time:
minutesPlaying = gameTime.TotalGameTime.Minutes; ;
secondsPlaying = gameTime.TotalGameTime.Seconds;
switch (currentGameState)
{
case GameState.MainMenu:
minutesMenu = gameTime.TotalGameTime.Minutes;
secondsMenu = gameTime.TotalGameTime.Seconds;
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
minutesTotal = minutesPlaying - minutesMenu;
secondsTotal = secondsPlaying - secondsMenu;
break;
}
Invoke my method:
timer.Update(minutesTotal, secondsTotal);
Update method:
public void Update(int min, int sec)
{
string seconds, minutes;
seconds = sec.ToString();
minutes = min.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Thanks for answer :)
I would use a 'StopWatch' to 'Start' and 'Pause' the tracking of time spent in your playing state. When the playing state is entered start the 'StopWatch' and then pause it when leaving that state.
Your on the right track, but I think you should have a time passed variable instead of having to subtract it from the original gameTime, this way if you go back to the menu, then back into the game, your method wont be broken. Basicly it just takes a TimeSpan at 0 and adds gameTime to it as time passes, giving you the amount of time spent in the level.
First off add a new TimeSpan
public TimeSpan TimePassed;
Now in your Update() method inside case GameState.Playing: you will need to incriment the timer.
TimePassed -= gameTime.ElapsedGameTime;
You can also reset the timer to Zero if you need to make a new level (If that applies, Ex: Replaying a level will need a timer reset)
Now to render the time in your Draw() method.
string timeString = TimePassed.Minutes.ToString("00") + ":" + TimePassed.Seconds.ToString("00");
spriteBatch.DrawString(FONT,timeString,POSITION,Color.White);
And there you go!
You should modify time only in Playing state, I have modified the code to achieve it, whithout too much changes... PlayTime keeps the total time elapsed in the playing state.
TimeSpan Elapsed ;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
Elapsed = TimeSpan.Zero;
break;
case GameState.Playing:
Elapsed = gametime.ElapsedGameTime;
break;
}
Invoke my method:
timer.Update(Elapsed);
Update method:
TimeSpan PlayTime = TimeSpan.Zero;
public void Update(TimeSpan Elapsed)
{
PlayTime.Add(Elapsed);
string seconds, minutes;
seconds = PlayTime.Seconds.ToString();
minutes = PlayTime.Minutes.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Try this:
Before the game starts you don't need to keep track of the time, so I have set minutesPlaying and secondsPlaying to 0 in GameState.MainMenu. Don't initialize minutesPlaying and secondsPlaying within the main Update() function.
minutesPlaying = 0;
secondsPlaying = 0;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
secondsPlaying += gameTime.ElapsedGameTime.Seconds;
if (secondsPlaying >= 60)
{
minutesPlaying++;
secondsPlaying = 0;
}
break;
}
When you display the time, just display minutesPlaying and secondsPlaying. You don't need any extra variables.
If that doesn't work, you can always keep track of the milliseconds (secondsPlaying += gameTime.ElapsedGameTime.Milliseconds) that pass when the game starts and divide the value by 1000 to display the seconds.

Categories