Space Engineers

Space Engineers

View Stats:
The Ant Farm Jun 14, 2016 @ 6:25pm
Programmable Block - Using buttons as conditional inputs (Also, setting delays in scripts?)
Hi,
While I'm only new to C#, I've got an alright idea of programming. I've run into a problem trying to use buttons as 'inputs' in scripts. I originally tried using IF loops where a button press would run the script with a specific argument and therefore prove TRUE to a specific loop. I would have thought there would be an easier solution than this.

I also can't seem to call a method within the Main() method that takes an argument as its input; I've found that called methods must be static. Does this mean and process dependent on the input argument must be directly written in the method? Additionally, unlike any other language I've done before, it doesn't seem to save variables that are defined within an IF loop. I want to write something similar to this:

if (argument == "Reset") {
Color Reset_colour = new Color(255,255,255);
}
else {
Color Reset_colour = new Color(0,0,255);
}
lights.SetVariable("Color", Reset_colour);

But instead I'm forced to write it like this with the action as part of each statement:

if (argument == "Reset") {
Color Reset_colour = new Color(255,255,255);
lights.SetVariable("Color", Reset_colour);
}
else {
Color Reset_colour = new Color(0,0,255);
lights.SetVariable("Color", Reset_colour);
}

If I write it the first way I get an error saying Reset_colour does not exist in this context. Would anyone have an idea on what causes this? Any help to these problems would be greatly appreciated.

PS. Is there a way of creating delays in scripts? Most languages support a wait, hold, sleep, etc, method but not the Space Engineers C# I'm told. Is there a solution around this? Apologies for the long post and thanks for the help.
< >
Showing 1-6 of 6 comments
plaYer2k Jun 14, 2016 @ 8:43pm 
Your problem with
if (argument == "Reset") { Color Reset_colour = new Color(255,255,255); } else { Color Reset_colour = new Color(0,0,255); } lights.SetVariable("Color", Reset_colour);
is that the variable Reset_colour is only defined within the scope of each { } bracket but not outside.

What you have to do is
Color Reset_color; if (argument == "Reset") { Reset_colour = new Color(255,255,255); } else { Reset_colour = new Color(0,0,255); } lights.SetVariable("Color", Reset_colour);


To create a delay you essentially got three ways:
1 count program executions
- that is a simple integer that increases per execution until a certain value is reached
- this approach has to be done at the "top level" of any PB
2 use timestamps to check how many reach seconds have been passed
- that is essentially defining a delay in [milli]seconds, have the last execution time and check if the current execution time is farther from the last time than the delay defines
- this approach however depends on real time and thus low sim speed has a negative impact unless manually corrected
- this approach works at any place and doesnt require a "tp level" implementation
3 use the ElapsedTime the PB defines
- this approach should be sim speed corrected but i dont know if that really is the case
- it has to be implemented at the "top level" as well


With "top level" i mean that it has to be done shortly after the void Main(string arg) start and before any actual heavy work or escape code is present.


I hope that helps
The Ant Farm Jun 14, 2016 @ 10:32pm 
That makes perfect sense why that works now, thank you plaYer2k! I thought it must be something like that (I even tried declaring the variable as public to attempt to recall it haha).

The first approach seems the simplest. Will this count script exectutions or can it be used to count loop iterations within a method? I could be wrong but one script execution happens every game 'tick' therefore you would have to re-run a script to count once as opposed to counting several times within one script?

Thanks for your help so far.
plaYer2k Jun 14, 2016 @ 10:41pm 
If you set your PB and timer up in such a way that the timer applies "Trigger Now" on itself as well as run the PB then the program will be ran once per tick.
However it can also be once every few ticks if it works with other methods or through a chain of multiple timers that work in sequence.
Additionally it could be a whole second if you make the timer "Start" itself with a 1s delay.

Thus if you have a construct like
int counter = 0; const int counterMax = 10; void Main(string arg) { if(++counter < counterMax) return; counter = 0; // actual code here ... }
then it counts each execution in whatever "delay" you defined through the timers.
However keep in mind that the PB could also get executed outside the expected usage.
That is either any PB that runs all other PBs it can get, using it through the terminal or buttons etc etc.
That is especially important when you got large delays in the timer but less so if the unexpected calls happen very rarely compared to the timer blocks calls.


You could of course also use that method for "loop iterations" but that is what the good old for-loop is for.
for(int i = 0; i < maxCount; i++) { //Do something with the variable i }
The Ant Farm Jun 15, 2016 @ 12:40am 
I have perhaps described my problem poorly. Instead of trying to run a an entire script every tick, I just want to run the script once and add a delay mid script. An example for this application could be using a button to trigger a piston (as well as other stuff for example) where the piston must retract a certain distance before the next action in the script can be performed, ie a 'x' second time delay is needed. I've just tried to make an example of it's use. Thank you for your replies
plaYer2k Jun 15, 2016 @ 2:25am 
The best for that would be a simple state machine.
https://en.wikipedia.org/wiki/Finite-state_machine

Have a state variable that defines where you currently are.
Have possible transitions for each state with depending conditions to move to another state with according actions.

Here are two examples how that could look like in a simple case:
http://flylib.com/books/4/70/1/html/2/images/fig405_01.jpg
http://i.stack.imgur.com/tLbXm.png

How you model your states is up to you then.
Lets assume a single PB with one argument "toggleDoor" that is meant to open or close a door with more precision than the simple open toggle as that doesnt really care about the real state.

You could build it with 3 or 4 states.
3 states => door open, door, closed, door changing
4 states => door open, door, closed, door opening, door closing

Also there is always an initial state, that is either a completely different state or you define one of the states as such. In the case of that door, it is best to have a separated initial state.

For the sake of examples lets do the 4 state version.
We use an integer "state" that gets associated with these states:
0 initial state
1 door open
2 door closing
3 door closed
4 door opening

The following code was not tested and serves as example
/* The state defines the current state of the state machine. 0 initial state 1 door open 2 door closing 3 door closed 4 door opening */ int state = 0; // This name is what the argument has to be in order to toggle the doors states. const string toggleText = "toggleDoor"; // The door that we want to control. IMyDoor door; // The name of the door we want to control. const string doorName = "Door"; void Main(string arg) { // Get the door block if we dont have it already. // Also return an error if we can not find one. if(door == null) { List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>(); door = GridTerminalSystem.GetBlocksOfType<IMyDoor>(blocks, x => x.CustomName == doorName); if(blocks.Count == 0) { Echo("Unable to find a door with the name: " + doorName); return; } door = blocks[0] as IMyDoor; blocks.Clear(); } // Check if the argument equals the toggleText once and store it in a bool. bool toggled = arg == toggleText; // State machine below // This is the initial state. It will essentially contain most transitions // that are reachable from whenever the PB starts running the first time. // You could design it to reach only when the door is fully opened/closed // or when the door is in transition too. But we keep it simple here. if(state == 0) { // Is the doors OpenRatio 0f then it is 0% open and thus closed // and thus we go to state 3 (door closed). if(door.OpenRatio <= 0f) state = 3; // Is the doors OpenRatio 1f then it is 100% open and thus open // and thus we go to the state 1 (door open). else if(door.OpenRatio >= 1f) state = 1; } // Do the two passive transition states first. if(state == 2 && door.OpenRatio == 0f) // door closing and fully closed { state = 3; } else if(state == 4 && door.OpenRatio == 1f) // door opening and fully closed { state = 1; } // Do the two active states last. if(toggled && state == 1) // door open and toggled { door.ApplyAction("Open"); // Yes the opened door toggles to "close" when applying "Open" state = 2; } else if(toggle && state == 3) // door closed and toggled { door.ApplyAction("Open"); state = 4; } }

I hope that helps.
Last edited by plaYer2k; Jun 15, 2016 @ 2:26am
The Ant Farm Jun 15, 2016 @ 6:24pm 
Thank you very much. I appreciate the time you've taken in responding to this thread. I think I have a better understanding of that now and might be able to apply it to what I'm doing.

Cheers
< >
Showing 1-6 of 6 comments
Per page: 1530 50

Date Posted: Jun 14, 2016 @ 6:25pm
Posts: 6