Add timer to idle game - c#

Ok, so as I go through learning C# I have run into an issue that I can't quite wrap my mind around.
I am building an idle game to learn more than books have taught me. Anyways, I am adding an "Auto-clicker" function. I thought that I should add a timer that would count to 1 second and add gold to the player's score. Maybe this is not the best way to approach this, but here is what I have so far:
Updated Code as per requested with Errors trying to load System.Timers.Timer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Timers;
namespace IdleClicker1
{
public partial class Form1 : Form
{
public double gold = 0;
public double goldPerClick = 1;
public double upgradeCost = 20;
public double autoMinerLevel = 0;
public double autoMinerCost = 10;
public System.Timers.Timer autoMineTimer = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
}
private void btnGetGold_Click(object sender, EventArgs e)
{
gold += goldPerClick;
updateGoldShown();
}
public void updateGoldShown()
{
lblGold.Text = "Gold: " + gold.ToString();
lblGoldPerClick.Text = "Gold per click: " + goldPerClick.ToString();
lblAutoMiner.Text = "Auto-Miner Level: " + autoMinerLevel.ToString();
}
private void btnUpgradeClick_Click(object sender, EventArgs e)
{
if (gold >= upgradeCost)
{
gold = gold - upgradeCost;
goldPerClick = goldPerClick + 1;
upgradeCost = upgradeCost + 10;
lblUpgradeCost.Text = "Cost to upgrade: " + upgradeCost.ToString();
updateGoldShown();
}
else
{
MessageBox.Show("Sorry bub... not enough gold yet!", "Error buddy!");
}
}
public void btnAutoMiner_Click(object sender, EventArgs e)
{
if (gold >= autoMinerCost)
{
autoMinerLevel++;
gold = gold - autoMinerCost;
autoMinerCost = autoMinerCost + 10;
btnAutoMiner.Text = "Buy Auto-Miner for: " + autoMinerCost.ToString();
updateGoldShown();
//Adding a new timer
System.Timers.Timer autoMineTimer = new System.Timers.Timer();
autoMineTimer.Tick += new EventHandler(timer_Tick);
autoMineTimer.Interval = 1000;
autoMineTimer.Enabled = true;
autoMineTimer.Start();
}
else {
MessageBox.Show("Sorry... not enough gold!", "Error again... yo!");
}
}
void timer_Tick(object sender, EventArgs e)
{
updateGoldShown();
btnGetGold.PerformClick();
}
}
}
Basically, this is a very simple setup. I am just trying to learn by practice and applying myself. So when the user clicks on btnAutoMiner, it should start a timer that would add whatever the goldPerClick to the player's gold. I don't want an exact answer (otherwise I will never learn), but can someone abstractly help me out?

I'm not sure what the point of the timer is. Do you want to run btnAutoMiner_Click each second or on each click? If you add on each click, what is the point of the timer? Sorry, it's just a bit hard to understand your goals.
Here is the timer documentation in case you needed it.
Edit: to perform a click from the timer, you can you use .PerformClick to simulate a click.

I mean this with the deepest respect but have you tried using the debugger and breakpoints to check if everything unfolds as expected.
I took the liberty to recreate your program, making a form that fits your code and what I got was a functional program that behaved as you described, when I click GetGold my gold increases by the goldPerClick value as expected, same with the UpgradeClick.
As I clicked Buy Auto-Miner I had a breakpoint at the buttons event method, the level went up by 1 as expected and the timer started just fine, again with a breakpoint I was monitoring the timers event method, which was called once per second as expected, so conclusion is that your program behaves just as its expected to logically.
However for the GUI there is a bit of a problem, the values is not being updated as the timer ticks so I would suggest looking there first, also some labels/buttons texts is only updated when certain buttons are clicked so again I would suggest putting all those in the same place and just call that method when needed.
Just some friendly design advice as well (without being too specific):
Consistency is important for good design, this means either use value1 += value2 or value1 = value1 + value2 both is equally as right but mostly for consistency.
Using the right value types, using int values for simple numbers like 10 (sbyte, byte, short, ushort, int, uint, or char) and use floating point values for values like 1.5 (Double, Float)
I really hopes these tips helps you along your way and good luck with the project.

Assuming that your GetGold button is working as you expect it to, you can programatically trigger it's click handler:
btnGetGold.PerformClick();
You would put this code inside the tick event handler for your Timer.

Related

Basic C# WPF Program - Converting string to double then adding variables in Textblock

Good evening all,
I've just started my second year at University and I'm learning C# for the first time. I've had some coding experience with Java and Javascript before but never c#.
I'm having problems with a basic credit/debit program. See code below:
private double balance = 0;
private string creditamount;
private string debitamount;
private double currentamount = 0;
private void CreditButton_Click(object sender, RoutedEventArgs e)
{
AmountField.Text = creditamount;
Convert.ToDouble(creditamount);
CurrentBalance.Text = "Your current balance is £" + (creditamount + balance);
}
private void DebitButton_Click(object sender, RoutedEventArgs e)
{
AmountField.Text = debitamount;
double.Parse(debitamount);
CurrentBalance.Text = "Your current balance is £" + (debitamount - balance);
}
The user should enter an amount into the AmountField and press either the Credit or Debit button. Upon clicking either button, the amount should be converted from a string into a double and then shown in the CurrrentBalance .Text
It apppears that my strings aren't being converted into doubles.
I've tried using Convert.ToDouble(); and double.Parse(); but Visual Studio keeps giving me errors.
Does anyone have any suggestions?
The = assignment goes from Right to Left. Also all Convert.ToStuff return the result, so you should be expecting it (assign it to some variable).
Therefore, your event handlers (and all your future routine) should look similar to this:
private void DebitButton_Click(object sender, RoutedEventArgs e)
{
debitamount= AmountField.Text ; // Take the Right and assign it to the Left
currentamount = Convert.ToDouble(debitamount); // convert + assign
CurrentBalance.Text = "Your current balance is £" + (debitamount - balance); // Again take the Right and assign it to the Left
}
First of all if you are using wpf try to avoid this code behind thing. Secondly try to take advantage of MVVM. What is MVVM. Go through this link.
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
So in short i would suggest create a Viewmodel class and make it implement INotifyPropertyChanged. Create your credit and debit amount properties bind it in the UI and don't forget to use OnPropertyChanged on these properties. So when ever user enters any text in the UI i.e when ever there is a change in the UI state your set block of the variables will be called and you can proceed further. Also in mvvm you use commands not button clicks. Also set the data context of you xaml to this view model.

C# windows forms reference control - NullReferenceException

Could anyone explain why I get a NullReferenceException, when I create a new Button and try to reference it? Creating the Button and assigning the Name works fine, but referencing it does not.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DragNDrop_1
{
public partial class Form1 : Form
{
//Variables----------------------------------------------------------------------
int ButtonID = 100;
bool isDraggingButton = false;
public Form1()
{
InitializeComponent();
}
//----------------------------------------------------------------------Variables
private void btn_addButton_Click(object sender, EventArgs e)
{
AddButton();
}
public void AddButton()
{
Button b = new Button();
b.Name = "Button" + ButtonID.ToString();
b.Text = "Button" + ButtonID.ToString();
b.Location = new Point(ButtonID, ButtonID);
ButtonID = ButtonID + 100;
pnl_DragNDrop.Controls.Add(b);
isDraggingButton = true;
}
private void DragTimer_Tick(object sender, EventArgs e)
{
if (isDraggingButton == true)
{
Point mouse = PointToClient(MousePosition);
this.Controls["Button" + ButtonID.ToString()].Location = new Point(mouse.X + 20, mouse.Y + 20);
}
}
}
}
The Exception occurs in the timer, where I try to reference the last button created. I read trough some threads regarding this Exception, but I still can't spot the error.
Yes, I know that this is very messy and I should propably create a custom Loop or de-/re- activate the timer, but this is just for testing purposes. Note that I'm new to C# and Windows Forms.
EDIT: As explained by Lukasz M, this is a Problem regarding Ownership (maybe the Term is not correct, it's the best german-english Translation I can come up with). This is neither the Focus of the Question from the Thread I "duplicated", nor is it mentioned in the Answer. If it is though, I have to question my English-skills. Anyway, I just wanted to make clear, that I indeed read the Thread, but wasn't able to spot a Solution. Maybe it's just the lack of English- and C#-Skills, but I'm pretty sure that this is not a duplicate.
It's because in AddButton method You create the button, but do not add it directly to the form's controls, but to the pnl_DragNDrop.Controls collection.
You can try to change this:
this.Controls["Button" + ButtonID.ToString()].Location = new Point(mouse.X + 20, mouse.Y + 20);
to this:
pnl_DragNDrop.Controls["Button" + ButtonID.ToString()].Location = new Point(mouse.X + 20, mouse.Y + 20);
and it should work fine.
Another way to do it would be saving the b button in a class field instead of a variable inside the method. This way, You could refer to the control in a different method without the need to find it by Id in the Controls collection. You may also want to add more than one button with different Id values, so the exact implementation for storing and refering to buttons created then, may depend on actual use case.
Update
To make the code actually work, please also notice that after You create the b control, You modify the variable used to compose its name:
ButtonID = ButtonID + 100;
Then, in DragTimer_Tick method You use the modified value to rebuild the control's name, but it's already different, so the control is not found.
When searching the control by name, You can either save the previous value of ButtonID or save the whole string used as button's name (as mentioned in the comments) to be able to use it to find the control later.

Timer doesn't seem to tick ( until I minimize window )

This is the image of the design window:
Here is the MainForm.Designer.cs file:
namespace SamsCSharp24
{
partial class ImeObrasca
{
// irrelavent code is omitted, only event subscriptions are left
private void InitializeComponent()
{
// irrelavent code is omitted for brewity
//
// SelectPicture
//
this.SelectPicture.Paint += new System.Windows.Forms.PaintEventHandler(this.SelectPicture_Paint);
this.SelectPicture.Click += new System.EventHandler(this.SelectPicture_Click);
//
// Quit
//
this.Quit.Click += new System.EventHandler(this.Quit_Click);
//
// PictureBox
//
this.PictureBox.MouseLeave += new System.EventHandler(this.PictureBox_MouseLeave);
this.PictureBox.MouseEnter += new System.EventHandler(this.PictureBox_MouseEnter);
//
// btnOptions
//
this.btnOptions.Click += new System.EventHandler(this.btnOptions_Click);
//
// timerClock
//
this.timerClock.Tick += new System.EventHandler(this.timerClock_Tick);
}
#endregion
}
}
Here is the MainForm.cs file:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SamsCSharp24
{
public partial class ImeObrasca : Form
{
public ImeObrasca()
{
InitializeComponent();
// when uncommenting below line, window is not seen in taskbar
// this.ShowInTaskbar = false;
}
// other code is omitted for brewity
private void SelectPicture_Paint(object sender, PaintEventArgs e)
{
// just for fun, change color of a button to light blue
SelectPicture.BackColor = Color.Azure;
}
private void timerClock_Tick(object sender, EventArgs e)
{
// when timer ticks, change label's text into current time of day
staticClock.Text = "Current time of day: " +
DateTime.Now.Hour.ToString() + " : " +
DateTime.Now.Minute.ToString() + " : " +
DateTime.Now.Second.ToString();
}
}
}
Timer control has following properties set via designer:
Enabled = true;
Interval = 1000
Name = timerClick
Tick (event) = timerClock_Tick
As for label, here are the properties also set with designer:
BorderStyle = FixedSingle
Name = staticClock
Autosize = false
Text =
Other properties are default or irrelevant ( like Location or Size )
PROBLEM:
When I run the application ( in Debug mode ), window appears with properly placed controls and with proper look. Every other part of the code works successfully ( picture opening / drawing etc ) but the label remains empty, as initially set in the designer.
After I minimize / maximize the window, the label text is set correct. I have tried to move the part of the window with label "out" of the screen and get it back to see what happens. The text in the label was changed sometimes -> it didn't update correct.
MY EFFORTS TO SOLVE THE PROBLEM:
This is my first time trying out C# and WinForms so I have tried to find some online documentation on timers.
After examining .Designer.cs file I have found out that the timer from toolbox belongs to System.Windows.Forms.Timer class. I found nothing there to help me, since in Remarks section is stated that setting property Enabled to true starts the timer, and setting it to false stops it.
I have tried to put simple message box, and it started popping properly when the window was minimized. When window is in normal state nothing showed, but other parts of the program worked well ( picture opening / drawing / etc ).
After trying to Google for solution / searching here on SO, I have found no concrete solution ( although some suggestions were made, but as I said, they weren't helpful to me ).
QUESTION:
How to modify timer's tick handler, so label's text can be modified every second?
What am I doing wrong?
As Hans Passant stated in one of the comments, "Paint event handlers should only paint, they should never change properties that cause the Paint event to be fired again. Such shenanigans cause the UI thread to burn 100% core, never getting to dispatch the low-priority synthesized messages. Like WM_TIMER. Minimizing the window stops that, temporarily."

saving variables between page refresh

i need to do a basic web page (homework)
the user need to input number into textbox. after every number he need to press the "enter number" button.
and after a few numbers he need to press a different button to display his number and some calculations.
i cant use arrays ( homework limitations).
the problem is that after every time the user press "enter number" , the variables reset.
what can i do?
here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
int totalSum=0;
int mulOdd=1;
int small=0;
int big=0;
float avg=0;
int pairsum=0;
int counter = 0;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
int t;
counter++;
t = Convert.ToInt16(TextBox1.Text);
totalSum = totalSum + t;
avg = totalSum / counter;
int odd = t % 2;
if (odd > 0)
{
mulOdd = mulOdd * t;
}
else
{
pairsum = pairsum + t;
}
if (t < small)
{
small = t;
}
if (t > big)
{
big = t;
}
TextBox1.Text = " ";`enter code here`
Label1.Text = Convert.ToString(counter);
}
protected void Button2_Click(object sender, EventArgs e)
{
Label1.Text = "total Sum" + Convert.ToString(totalSum);
}
}
}
thank you.
You need to save the variables in state. There are several ways to pass variables
[1] Cookies (rarely found use for this)
[2] Session (Good for storing between pages for a different user)
[3] ViewState (Same page saving for a user)
[4] Query Strings (passing between pages)
Have a read about these and this should do the trick. Forgive me if i missed some out.
You need to read about the page life cycle and about state management, in particular view state.
It's difficult to help without knowing the conditions of your assignment, but a method that would be usable would be to have a hidden field (webusercontrol) on the page that would hold a string. Each time the button is clicked, append the string representation of the number to the value of that control, making sure to separate the values with a symbol (comma, #, |, anything will do really). Then at any point during the process you would be able to get the value of that control, split it on the character, and compute whatever you needed to compute.
Since this is identified as homework, I won't be posting code, but this process should be pretty straight-forward.
In your page load event, check the Page.IsPostback property. If true, then reset your variables based on the user input from the server controls HTML markup.
Read the links provided by #Oded. Page life cycle is a very important topic.
Some pointers that you could extend to all your variables...
Remove the global declaration of all of these variables, you don't need to have a global scope for them:
int totalSum=0;
int mulOdd=1;
int small=0;
int big=0;
float avg=0;
int pairsum=0;
int counter = 0;
On Button1_Click store the totalSum as so:
ViewState["Sum"] = Convert.ToInt16(string.IsNullOrEmpty(ViewState["Sum"])?"0":ViewState["Sum"].ToString() )+ Convert.ToInt16(TextBox1.Text);
Finally, on Button_Click2 do:
Label1.Text = "total Sum" + Convert.ToString(string.IsNullOrEmpty(ViewState["Sum"])?"0":ViewState["Sum"].ToString());

Slide object using For Loop (C#)

I did quite a bit of searching around and didn't find anything of much help.
Is it possible to "slide" or "move" using C#, an object from one Location to another using a simple For loop?
Thank you
I would suggest you rather use a Timer. There are other options, but this will be the simplist if you want to avoid threading issues etc.
Using a straight for loop will require that you pump the message queue using Application.DoEvents() to ensure that windows has the opportunity to actually render the updated control otherwise the for loop would run to completion without updating the UI and the control will appear to jump from the source location to the target location.
Here is a QAD sample for animating a button in the Y direction when clicked. This code assumes you put a timer control on the form called animationTimer.
private void button1_Click(object sender, EventArgs e)
{
if (!animationTimer.Enabled)
{
animationTimer.Interval = 10;
animationTimer.Start();
}
}
private int _animateDirection = 1;
private void animationTimer_Tick(object sender, EventArgs e)
{
button1.Location = new Point(button1.Location.X, button1.Location.Y + _animateDirection);
if (button1.Location.Y == 0 || button1.Location.Y == 100)
{
animationTimer.Stop();
_animateDirection *= -1; // reverse the direction
}
}
Assuming that the object you're talking about is some kind of Control you could just change the Location property of it.
So something like this:
for(int i = 0; i < 100; i++)
{
ctrl.Location.X += i;
}
Should work I think.

Categories

Resources