I am making a very simple multiple choice console game. I have created a finite state machine using enums and if else states all within a while loop (while game = true).
The issue is, inside of the class level.level1 (contains level info) it needs to change myState to the new state which is in the main program, but it can't impact it. Any ideas on how to make this happen? I'm stumped.
var game = true;
while (game = true)
{
State myState = State.intro;
//first level
if (myState == State.intro)
{
level.Intro();
}
else
{
if (myState == State.contact1)
{
Console.WriteLine("Wroked");
}
in the level class:
Console.WriteLine(name + " What are you doing, I need your help!");
Thread.Sleep(1000);
Console.WriteLine("\nA. Who is this? \nB. How are you doing this?\nC. How do you know my name?");
var res = Console.ReadLine();
char response = res[0];
switch (response)
{
case 'a':
State myState = State.contact1;
break;
case 'b':
Console.WriteLine("\nright");
break;
case 'c':
Console.WriteLine("\nleft");
break;
}
There's obviously more code than this, but that's the important part. you can see full code here:
https://gist.github.com/MajorMojo/700936c02f1430de44b4559f0579917d
https://gist.github.com/MajorMojo/5bba21cb977a4b768029e765a0d74886
you either need to pass a game object into the level class or make some static variables.
I would do the first one and have a GameEngine class that is created by main and maintains the overall state of the game. It can have a SetState method (or State property). Pass the GameEngine object into the level when you create it.
I would suggest starting with a basic intro to programming tutorial. The C# one isn't too bad: https://msdn.microsoft.com/en-us/library/aa664628(v=vs.71).aspx
You're missing a lot of basic concepts. It would be a lot easier for you to pick them up by working through a complete tutorial than by asking individual questions here.
Related
I'm making a game. it has a login panel and rememmer me button. i keep the info on regedit. when I click rememmer me button it works and when I start game after click button it gets my info like id and pass but when I start again it deletes my id and pass on regedit. So it get just one time. I couldn't find the problem or fix way. Can you help me? Thank you.
Here is my codes:
void Start()
{
if(PlayerPrefs.GetInt("BeniHatirla")==1)
{
Debug.Log("start "+PlayerPrefs.GetInt("BeniHatirla"));
BeniHatirlaGetir();
}
}
Here is the method ı called in start:
public void BeniHatirlaGetir()
{
isim = PlayerPrefs.GetString("BeniHatirlaIsim");
sifre = PlayerPrefs.GetString("BeniHatirlaSifre");
Debug.Log("kullanici "+isim+sifre);
BeniHatirlaUniSlider.value = 1;
Debug.Log("Ogrenm Durumu"+PlayerPrefs.GetInt("OgrenimDurumuBelirleme"));
switch (PlayerPrefs.GetInt("OgrenimDurumuBelirleme"))
{
case 1:
OrtaOkulKadiTextBox.text = isim.ToString();
OrtaokulKsifreTextBox.text = sifre.ToString();
LoginPanelleri[0].SetActive(true);
break;
case 2:
LiseKadiTextBox.text = isim.ToString();
LiseKsifreTextBox.text = sifre.ToString();
LoginPanelleri[1].SetActive(true);
break;
case 3:
UniversiteKadiTextBox.text = isim.ToString();
UniversiteKsifreTextBox.text = sifre.ToString();
LoginPanelleri[2].SetActive(true);
break;
}
}
And here is the rememmer me button:
public void BeniHatırlaButon()
{
PlayerPrefs.SetInt("BeniHatirla", 1);
switch (PlayerPrefs.GetInt("OgrenimDurumuBelirleme"))
{
case 1:
PlayerPrefs.SetString("BeniHatirlaIsim", OrtaOkulKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", OrtaokulKsifreTextBox.text.ToString());
break;
case 2:
PlayerPrefs.SetString("BeniHatirlaIsim", LiseKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", LiseKsifreTextBox.text.ToString());
break;
case 3:
PlayerPrefs.SetString("BeniHatirlaIsim", UniversiteKadiTextBox.text.ToString());
PlayerPrefs.SetString("BeniHatirlaSifre", UniversiteKsifreTextBox.text.ToString());
break;
}
}
If you're running some debugger, it may be missing out on when it actually saves the values to disk. From the documentation for PlayerPrefs:
By default Unity writes preferences to disk during OnApplicationQuit(). In cases when the game crashes or otherwise prematuraly exits, you might want to write the PlayerPrefs at sensible 'checkpoints' in your game. This function will write to disk potentially causing a small hiccup, therefore it is not recommended to call during actual gameplay.
So if you are exiting the application prematurely through debugging it may be that you just need to call PlayerPrefs.Save() right after you try to write the values.
So I'm working on a Switch-Case menu for my program but I'm having multiple issues (I'm probably missing something real obvious here)
So first off I'm trying to implement a while loop to make it possible to return to the menu after executing any of the case methods. However when trying to implement a while loop it doesn't seem to recognise my bool variable for some reason.
Secondly I'm not quite sure how to make it so the user can return to the start menu after they've done what they want to do in the case they've chosen, this probably has a real easy solution, but I just can't find one.
[code]
private string[] säten = new string[24];
private int Antal_passagerare = 0;
public void Run()
{
bool continue = true;
while(continue)
{
string menu = (Console.ReadLine());
int tal = Convert.ToInt32(menu);
switch(tal)
{
case 1:
Add_passagerare;
break;
case 2:
break;
case 3:
break;
}
}
}
[/code]
Your problem is that your local variable name conflicts with the C# keyword (or statement) continue which controls the flow of a loop (e.g. for, foreach, while, etc). Another control flow keyword is break.
You must rename the local variable. But because of the flow control keywords you can drop the local variable (see below). Also use Int32.TryParse to avoid your program from crashing, if the user inputs a non numeric value. In this context you can see the statements continue and break at work:
// Start an infinite loop. Use the break statement to leave it.
while (true)
{
string userInput = Console.ReadLine();
// Quit if user pressed 'q' or 'Q'
if (userInput.Equals("Q", StringComparison.OrdinalIgnoreCase)
{
// Leave the infinite loop
break;
}
// Check if input is valid e.g. numeric.
// If not show message and ask for new input
if (!(int.TryParse(userInput, out int numericInput))
{
Console.WriteLine("Only numbers allowed. Press 'q' to exit.");
// Skip remaining loop and continue from the beginning (ask for input)
continue;
}
switch (numericInput)
{
case 1:
break;
case 2:
Add_passagerare();
break;
case 3:
break;
}
}
EDIT: I see I'm getting a lot of downvotes on this post. I've tried to explain what I try to do, where my errors and in which direction want to go. I'm asking for insight in what I'm doing wrong. If you downvote; pleas tell me why, so I can improve the question. Thanks.
I'm creating an application where I have one main form, and several different User Controls which the user works on. This helps me splitting the code, managing the different parts of the program. And it would be easy to expand the application later on.
I'm trying to create a class where I manage the active controls I want to call one function in that class with as argument the form that should become active.
An image can illustrate how I try to setup this application. Note that the control manager class is not a seperate class in the code i show below, but a partial class of the mainform. Any tips on how to get it like in the image are very welcome :)
The class to manage all active forms looks like this: Please note that all user controls are just a user control with some buttons/textboxes etc on it. No code is added at all yet.
public partial class STP2Main
{
// I make each UserControl accessable for the whole class
SetupDeviceControl.SetupDevice SetupDev = new SetupDeviceControl.SetupDevice();
GenConfigFileControl.GenConfigFileControl GenConfFile = new GenConfigFileControl.GenConfigFileControl();
Monitoring.MonitoringControl Monitor = new Monitoring.MonitoringControl();
GenEncKeyControl.GenEncKeyControl GenEncKey = new GenEncKeyControl.GenEncKeyControl();
MenuControl.MenuControl MenuControl = new MenuControl.MenuControl();
public void SelectActiveWindow()
{
// Any active control should be hidden thats what this function does:
HideCurrentActiveControl();
// Check whether the window is already created
if (!WindowExists())
{ // if not created; create the windows:
switch (STP_Design.ProgramParameters.C.NextActiveControl)
{
case STP_Data.Data.SetupDeviceControl: // control 1:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.SetupDeviceControl;
STP_Design.ProgramParameters.C.SetupDeviceControlIsCreated = true;
SetupDev.Parent = this;
SetupDev.Location = new Point(3, 30);
SetupDev.Show();
SetupDev.BringToFront();
break;
case STP_Data.Data.MonitoringControl: //control 2:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.MonitoringControl;
STP_Design.ProgramParameters.C.MonitoringControlIsCreated = true;
Monitor.Parent = this;
Monitor.Location = new Point(3, 125);
Monitor.Show();
Monitor.BringToFront();
break;
case STP_Data.Data.MenuControl: // control 3
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.MenuControl;
STP_Design.ProgramParameters.C.MenuControlIsCreated = true;
MenuControl.Location = new Point(3, 30);
MenuControl.Parent = this;
MenuControl.Show();
MenuControl.BringToFront();
break;
}
}
else
{ // window is already created so needs to be called to front again:
switch (STP_Design.ProgramParameters.C.NextActiveControl)
{
case STP_Data.Data.SetupDeviceControl:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.SetupDeviceControl;
SetupDev.BringToFront();
break;
case STP_Data.Data.MonitoringControl:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.MonitoringControl;
Monitor.Visible = true;
Monitor.BringToFront();
break;
case STP_Data.Data.AdvancedMenu:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.AdvancedMenu;
tabControl1.Visible = true;
tabControl1.BringToFront();
break;
case STP_Data.Data.MenuControl:
STP_Design.ProgramParameters.C.CurrentActiveControl = STP_Data.Data.MenuControl;
MenuControl.Visible = true;
MenuControl.BringToFront();
break;
}
}
btnMenu.BringToFront();
}
// some functions which are called above are not shown; not relevant for this question
}
What I experience is the following: I get no errors at all. But the controls simply not change at all. If I call a window, it is created only once, because I did make it as partial class of my Mainform. (I've tried a complete seperate class, which did result in errors with threading, As I am not an experienced c# programmer, I tried to avoid that using a partial class.)
I'll add another function; which does not do anything at all:
private void HideCurrentActiveControl()
{
switch (STP_Design.ProgramParameters.C.CurrentActiveControl)
{
case STP_Data.Data.SetupDeviceControl:
SetupDev.Visible = false;
break;
case STP_Data.Data.MonitoringControl:
tabControl1.Visible = false;
Monitor.Visible = false;
break;
case STP_Data.Data.GenConfFileControl:
GenConfFile.Visible = false;
break;
case STP_Data.Data.GenEncKeyControl:
GenEncKey.Visible = false;
break;
case STP_Data.Data.MenuControl:
MenuControl.Visible = false;
break;
case STP_Data.Data.AdvancedMenu:
tabControl1.Visible = false;
break;
default:
tabControl1.Visible = false;
break;
}
}
I've tried debugging this part of code and it executes the statements, but I see no changes at all.
I think I've shown what I am trying to do; and how I try to do that. My question is: How do I acces those forms so I can manage them from a seperate class (or in this case partial class of the main form).
Then I have this last function, which does some wierd things. Before I call the SelectActiveWindow() function I update the variable STP_Design.ProgramParameters.C.NextActiveControl to for example: ...AdvancedMenu. (this was before that ...MenuControl) But it does always show that it is still MenuControl. Nowhere in my code is something where I change that value besides right before I start the function. (I've also tried to make the nextcontrol as an argument of the function SelectActiveWindow() but this did the same)
private bool WindowExists()
{
switch (STP_Design.ProgramParameters.C.NextActiveControl)
{
case STP_Data.Data.SetupDeviceControl:
if (STP_Design.ProgramParameters.C.SetupDeviceControlIsCreated)
return true;
else
return false;
case STP_Data.Data.MonitoringControl:
if (STP_Design.ProgramParameters.C.MonitoringControlIsCreated)
return true;
else
return false;
case STP_Data.Data.GenConfFileControl:
if (STP_Design.ProgramParameters.C.GenConfFileIsCreated)
return true;
else
return false;
case STP_Data.Data.GenEncKeyControl:
if (STP_Design.ProgramParameters.C.GenEncKeyControlIsCreated)
return true;
else
return false;
case STP_Data.Data.AdvancedMenu:
return true;
case STP_Data.Data.MenuControl:
if (STP_Design.ProgramParameters.C.MenuControlIsCreated)
return true;
else
return false;
default:
return false;
}
}
Summery of what I am looking for:
I am having a main form where display different user controlls in. I am trying to create a seperate class which is accessable from each control/form in my project. This class should manage the controls which are shown. In the code above I illustrated how I tried to do this, but this does not result in the expected result.
Ok, Now I understand the context needed. We actually do something very similar in my program. Here is a basic outline of how we do it...
Layout
On the main form we have a Panel container that we call pnlMain. It is this control that we add and remove active user controls from. We also have a UserControl object at a global level on the form representing curActiveControl.
Code
When the user selects a window via one of the menu's, we run a function that looks like this
switch (UserSelection)
{
case "Page 1":
if(curActiveControl.GetType() != typeOf(Page1Control))
{
pnlMain.Controls.Remove(curActiveControl);
curActiveControl = new Page1Control();
//do setup and configuration things
pnlMain.Controls.Add(curActiveControl);
}
//do some post processing things
break;
//other pages/specific page controls
}
Refresh();
The downside to this specific method is that the pages themselves are not persistent, so if there are entries or variables you want to have active across a session rather than only while on a page, you have to store them in some other global object and reload them from the user control's Load or Constructor methods.
You could do this same thing but instead of creating a new control instance each time for curActiveControl you could simply replace it with the standby instance of the new control. Be careful with referencing and overwriting though, its not something I personally have tried before.
The key in the method we use is the Panel that holds the user controls. Rather than adjusting the visibility and Z-Order of a large number of user controls, we simply change the displayed control in the main panel and the other control don't even exist at any given point in time.
The other wrinkle is that this functionality is directly on our Main Form. I Don't know how well this will work as another partial class. Its definitely worth a try though.
I'm using a switch as a state manager for my XNA game. The switch is a part of main update method, so it's run every frame. Sometimes I need to set a timer value and it should only be set once per method call. There are multiple methods that set the timer per case, so it can't use the current and previous state numbers to check if it's ok to overwrite previous time.
case "state 34": {
SetTime(theTime); // should run only once
// other things
if (TheTimeisRight(time)) // runs every call
{
SetTime(theTime); // should run only once
if (TheTimeisRight(time))
{ /* some methods */ }
}
break; }
How can I make this work, or is there a better way to do this without going outside the switch? (changing SetTime method is ok, but I wouldn't like to clutter up the switch with additional code)
Another method: Introduce a wrapper around the method you want to call:
public sealed class RunOnceAction
{
private readonly Action F;
private bool hasRun;
public RunOnceAction(Action f)
{
F = f;
}
public void run()
{
if (hasRun) return;
F();
hasRun = true;
}
}
Then create var setTimeOnce = new RunOnceAction(() => SetTime(theTime)); before the switch statement, and call there as setTimeOnce.run(). Adjust for parameters/return values as necessary.
If you don't want to mess with boolean variables ala hasSetTimeAlready, you can always introduce another state that calls the method, then proceeds to the original state.
Put the call outside the loop.
You might need a separate conditional statement to determine whether it should run at all, but that's got to be infinitely better than trying to use flags and/or various other smelly-code approaches to control repetitions of the call.
Edit:
here is what I mean by putting it in one place outside of the switch:
if (someCondition && someOtherCondition && yetAnotherCondition)
setTime(theTime); // just one call, in one place, gets executed once
switch(someValue)
{
case "state 34": {
//SetTime(theTime); // no longer necessary
// other things
if (TheTimeisRight(time)) // runs every call
{
//SetTime(theTime); // no longer necessary
if (TheTimeisRight(time))
{ /* some methods */ }
}
break;
...etc...
}
A word of advice: use an enumeration for your switch value rather than a string.
To be brutally honest, this is about as much as anyone can realistically help you with this without seeing a more complete code sample (I think the sample you gave us is somewhat contrived and not quite accurate to what you have?). Chances are that the best way to get round this problem is to deconstruct the switch statement and start again because either maintaining a state machine is not the best way to handle this situation or you need to introduce some other states.
I have resorted to using HashSet<int> to check if the current SetTime(time, num) method has not been called before with if (!hashSet.Contains(num)).
void SetTime(int time, int num)
{
if (!hashSet.Contains(num))
{
theTime = time;
hashSet.Add(num);
}
}
Sure doesn't look too cool, but works and it doesn't damage method call too much (visually), so the switch's readability is saved.
I tried this:
if(PauseButton.Image != global::GripAIConsole.Icons.Resume)
{
PauseButton.Image = global::GripAIConsole.Icons.Resume;
ToolTipMainWin.SetToolTip(PauseButton, "Resume / Step <F4>");
}
And it doesn't work. I would have thought it was comparing pointers internally to see if they were pointing at the same place.
You are working with resources, so if you want to make sure the reference is right, compare using the static method Object.ReferenceEquals(). As ChrisF said, you should not use this to determine application logic. Using a simple bool variable is much better (and slightly more performance friendly).
I would define the possible states with an enum:
public enum State {
Stopped,
Pausing,
Running
}
And then define a state property or variable:
State _state;
then Change the states as follows:
void ChangeState(State newState)
{
_state = newState;
switch (newState) {
case State.Stopped:
PauseButton.Image = global::GripAIConsole.Icons.Pause;
ToolTipMainWin.SetToolTip(PauseButton, "Start game <F5>");
break;
case State.Pausing:
PauseButton.Image = global::GripAIConsole.Icons.Resume;
ToolTipMainWin.SetToolTip(PauseButton, "Resume / Step <F4>");
break;
case State.Running:
PauseButton.Image = global::GripAIConsole.Icons.Pause;
ToolTipMainWin.SetToolTip(PauseButton, "Pause <F4> / Stop game <F6>");
break;
}
}
... or whatever your logic requires.
This is much cleaner and understandable. A picture has to do with the GUI, not with the logic. Inferring the state of the logic from things displayed on the forms is weird.