SKAction has waiting for duration abilities, for a period of time on a node. And seems to perform actions on nodes. Like moveTo, etc.
If I don't want that, rather I'd prefer to call functions within GameScene after a period of time, how do I do that with SpriteKit in the GameScene, not on a Sprite or other Node?
Are SKActions the way to do this? The only way to do this?
Yes. This question IS that ridiculously simple. I lack the heuristics and terminology to find an answer. Just keep looping around on how SKAction waits are calls on SKSprites for things like scale, rotation, etc, after time. Which isn't want I want/need.
Update:
Desired outcome, inside GameScene
doSetupStuff() // does some stuff...
waitForAWhile() // somehow wait, perhaps do somethings in here, while waiting
doSomethingElse() // does this after the waitForAWhile has waited
UPDATE 2:
What I think happens, again, inside didMove(to view...)
func wait(){
let timeToPause = SKAction.wait(forDuration: 3)
run(timeToPause)
}
let wontwait = SKAction.wait(forDuration: 3)
run(wontwait)
thisFunction(willnot: WAIT"it starts immediately")
wait()
thisFunction(forcedToWait: "for wait()'s nested action to complete")
UPDATE 3:
Found a way to get the delay without using SKActions. It's a little crude and brutal, but makes more sense to me than SKActions, so far:
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
print("I waited ten seconds before printing this!")
}
An option, as you cited, is to manage this externally. The way I typically manage this sort of thing is to have an externally run update cycle. One that
To drive this updater, you could use either CADisplayLink (which is what I use right now with my OpenGL renderer) or a dispatch source timer (which I have used with my SpriteKit engine). When you use an updated, you want to calculate the delta time. The tick handler could look something like:
func tickHandler() {
let currTime = NSDate().timeIntervalSince1970
let dt = lastTime - currTime // lastTime is a data member of the class
// Call all updaters here, pretend "updater" is a known updater class
updater.update(dt)
}
And updater's update method would look something like:
func update(deltaTime:NSTimeInterval) {
// Do your magic
}
I typically have a main overall updater running independent of what people are calling scenes. Example usage would be something like having an attract mode like in old school arcade games. There they show title screen, sample game play, high scores, rinse and repeat. Scenes would be title, game play, high score. Here you can your main updater manage the time and coordinate the construction/destruction/switching of the scenes. Note this implies having an overall scene manager (which is actually quite handy to have).
For your case, you could use this updater to drive the GameScene updater. It's updater could look something like:
func update(deltaTime:NSTimeInterval) {
switch state {
case .SetupState:
// noop?
println("I'm in setup") // Shown just so you can see there is a setup state
case .WaitState:
waitTime += deltaTime
if waitTime >= kWaitTime {
// Do whats you gots to do
doSomethingElse()
state = .NextState
}
case .NextState:
// blah blah blah blah
}
}
So the flow to do this call path from your driver (CADisplayLink or dispatch source) would be something like:
tickHandler -> master updater -> game scene updater
Some will def find this is perhaps a little heavy handed. I, on the other hand, find this very helpful. While there is obviously some time management and the loss of being able to fire and forget, it can help provide more control for orchestrating pieces, as well as arbitrarily changing state without having to worry about killing already queued actions. There is also nothing that says you still cannot mix SKAction. When I did use SpriteKit, I did all my updating this way along with some dispatched items. I only used SKAction to update hierarchy. Keep in mind that I used my own animation and physics system. So at least for me I had a lot less dependency on SpriteKit (it effectively was just a renderer for me).
Note you have to have your own means to handle pause and coming to foreground where your timer will need to be resynced (you only need to worry about tickHandler). Breakpoints also will cause time jumps.
You can use below function
#define ANIM_TIME 2
SKAction *customACtion = [SKAction customActionWithDuration: ANIM_TIME actionBlock:^(SKNode *node, CGFloat elapsedTime) {
// Do Something Here
}];
Another way to make something happen after a certain period of time is to make use of the 'current time' parm passed to update(). The following code will spawn a boss at intervals ranging from 20 to 30 seconds.
In your property definitions:
var timeOfLastBoss: CFTimeInterval = -1 //Indicate no boss yet
var timePerBoss = CFTimeInterval()
.
.
.
didMoveToView() {
...
timePerBoss = CFTimeInterval(Int.random(20...30))
'''
}
.
.
.
func update(currentTime: CFTimeInterval) {
...
spawnBossForUpdate(currentTime)
...
}
'
'
'
func spawnBossForUpdate(currentTime : CFTimeInterval) {
if ( timeOfLastBoss == -1 ) {timeOfLastBoss = currentTime}
if (currentTime - timeOfLastBoss < timePerBoss) {return}
// Rest of 'spawnBoss code
self.timePerBoss = CFTimeInterval(Int.random(20...30))
self.timeOfLastBoss = currentTime
}
One way, using SKActions, in Swift 3.0, looks like this:
DEFINE: aPatientlyWaitingFunction() at the top level of
GameScene class.
To cause a delay to happen before calling the above function, inside
didMove(to view...)
three ways I've found to do this using Actions:
All three ways seem to accomplish the exact same thing:
let timeToWait: TimeInterval = 3 // is seconds in SKAction thinking time
let waitSomeTime = SKAction.wait(forDuration: timeToWait)
// 1st way __________________________________________
// with a completion handler, the function can be called after Action
run(waitSomeTime) {self.aPatientlyWaitingFunction()}
// 2nd way __________________________________________
// as a completion to be done after action, in the run invocation:
run(waitSomeTime, completion: aPatientlyWaitingFunction)
// 3rd way __________________________________________
// alternatively, as part of a sequence of actions...
// Create a sequence, by making a run action from waitSomeTime and...
let thenDoThis = SKAction.run(aPatientlyWaitingFunction)
// then activate sequence, which does one action, then the next
run(SKAction.sequence([waitSomeTime, thenDoThis]))
// OR... for something different ____________________
////////////////////////////////////////////////////////
DispatchQueue.main.asyncAfter(deadline: .now() + timeToWait) {
self.aPatientlyWaitingFunction()
print("DispatchQueue waited for 3 seconds")
}
I'm quite new to scilab, I have created two GUIs (see example below), with script 2 being called from script 1. However the function in script 2 don't seem to work. Can anyone help?
Script 1
'//////////
f=figure('figure_position',[0,0],'figure_size',[1250,650]);
//////////
delmenu(f.figure_id,gettext('File'))
delmenu(f.figure_id,gettext('?'))
delmenu(f.figure_id,gettext('Tools'))
toolbar(f.figure_id,'off')
handles.dummy = 0 ;
handles.exam=uicontrol(f,'unit','normalized','BackgroundColor', [0.5,1,1],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[14],'FontUnits','points','FontWeight','bold','ForegroundColor',[0,0.5,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.5,0.5,0.1,0.05],'Relief','flat','SliderStep',[0.01,0.1],'String','exam','Style','pushbutton','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','obj102','Callback','exam_callback(handles)')
function exam_callback(handles)
close(f);
clear
exec('costs0-1.sce',-1) ;
endfunction`
Script 2
////////// Defining the figure (size, name etc)/////////////////////////////
f=figure('figure_position',[0,0],'figure_size',[1250,650],'auto_resize','on','background',[8]);
//////////
delmenu(f.figure_id,gettext('File'))
delmenu(f.figure_id,gettext('?'))
delmenu(f.figure_id,gettext('Tools'))
toolbar(f.figure_id,'off')
//Cabinet - TEXT
handles.obj17=uicontrol(f,'unit','normalized','BackgroundColor',[1,1,1],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[12],'FontUnits','points','FontWeight','normal','ForegroundColor',[0,0,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.15,0.93,0.1,0.05],'Relief','flat','SliderStep',[0.01,0.1],'String','Cabinet','Style','text','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','obj17','Callback','')
// Cabinet - POP UP MENU
handles.service=uicontrol(f,'unit','normalized','BackgroundColor',[0.8,0.8,0.8],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[12],'FontUnits','points','FontWeight','normal','ForegroundColor',[0,0.5,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.25,0.93,0.15,0.05],'Relief','flat','SliderStep',[0.01,0.1],'String','1|2','Style','popupmenu','Value',[1],'VerticalAlignment','middle','Visible','on','Tag','service','Callback','service_callback(handles)')
// CALCULATE PUSHBUTTON
handles.Calculate=uicontrol(f,'unit','normalized','BackgroundColor',[0,0.8,0],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[16],'FontUnits','points','FontWeight','bold','ForegroundColor',[0,0,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.22,0.02,0.15,0.08],'Relief','raised','SliderStep',[0.01,0.1],'String','CALCULATE','Style','pushbutton','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','Calculate','Callback','Calculate_callback(handles)')
// Resources- TEXT
handles.Resourcestxt=uicontrol(f,'unit','normalized','BackgroundColor',[1,1,1],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[14],'FontUnits','points','FontWeight','bold','ForegroundColor',[0,0.5,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.75,0.95,0.20,0.05],'SliderStep',[0.01,0.1],'String','Resources in hours','Style','text','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','','Callback','')
// TOTAL hours - TEXT
handles.totalhourstxt=uicontrol(f,'unit','normalized','BackgroundColor',[1,1,1],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[14],'FontUnits','points','FontWeight','bold','ForegroundColor',[0,0.5,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.75,0.75,0.12,0.05],'SliderStep',[0.01,0.1],'String','Total Hours','Style','text','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','','Callback','')
// hardware hours - text
handles.totalhours=uicontrol(f,'unit','normalized','BackgroundColor',[0.95,1,1],'Enable','on','FontAngle','normal','FontName','helvetica','FontSize',[14],'FontUnits','points','FontWeight','bold','ForegroundColor',[0,0.5,0],'HorizontalAlignment','center','ListboxTop',[],'Max',[1],'Min',[0],'Position',[0.88,0.75,0.08,0.05],'SliderStep',[0.01,0.1],'String','','Style','text','Value',[0],'VerticalAlignment','middle','Visible','on','Tag','totalhours','Callback','')
function Calculate_callback(handles)
if handles.service.value == 1 then
resource_hrs = 2
end
if handles.service.value == 2 then
resource_hrs = 10
end
set(handles.totalhours,'String',string(resource_hrs));
endfunction
Problem
It is a scoping problem. When function exam_callback() gets called it runs the other script with exec('costs0-1.sce',-1).
In that script you define the function Calculate_callback(handles). This goes out of scope and is deleted when exam_callback() is finished and as such can't be called when the button is pressed.
The second problem is that the handles are not globally affected, so when leaving exam_callback() the handles of the second Cost Gui are not added to handles.
Solution
You can move the generating of the GUI into a function createCostGui() and then load script2 at the start of script1 with exec('costs0-1.sce',-1);.
To make Calculate_callback(handles) function discard the handles argument and use the tags to find the handles
function Calculate_callback()
serviceHandle = findobj('tag','service');
if serviceHandle.value == 1 then
resource_hrs = 2
end
if serviceHandle.value == 2 then
resource_hrs = 10
end
totalHoursHandle = findobj('tag','totalhours');
set(totalHoursHandle,'String',string(resource_hrs));
endfunction
Further remarks
Text elements are generally static and thus don't need a Callback argument.
If you want an argument to stay at its default value, you don't need to specify them.
From the Scilab documentation:
h = uicontrol(PropertyName, PropertyValue,...) creates an uicontrol and assigns the specified properties and values to it. It assigns the default values to any properties you do not specify. The default uicontrol style is a "Pushbutton". The default parent is the current figure. See the Properties section for information about these and other properties.
Small remark on your question
Next time an error message could help with making your question more specific.