I am working on a game where the rate of change of an image changes over time. At first the image change every two seconds and then it will speed up.
var change = 2.0
func setupGame(){
change = 2.0
changeImage()
}
func changeImage(){
timer = NSTimer.scheduledTimerWithTimeInterval(change, target: self, selector: #selector(SecondViewController.changeColors), userInfo: nil, repeats: true)
change -= 0.1
}
The problem is that it speeds up too much and it doesn't stop. Eventually the change value becomes negative.
Any way to make this work?
You are forgetting two things.
First, you are creating a new repeating timer every time the timer fires. You need to invalidate the old timer before creating the new timer. And since you are going to replace the timer anyway, you do not want this to be a repeating timer; you need to say false instead of true.
Second, if you don't want change to keep getting smaller, you need some sort of condition where you check how small change has become and, if you don't want to get any faster, don't decrement it.
Related
I'm writing an OS X app that records real time events that come from a serial port and saves them to disk. So each millisecond I get 7 integers that I need to save and later "play back" by drawing them in a sort of live line chart. The saving stuff to disk is already done; playing them back is where I'm stuck.
I've done this sort of thing before with other technologies (Arduino, Corona, Processing) where I get a main game loop in which I can do whatever I want time-wise. What I need to know is how set up some sort of 'onEnterFrame' loop to be able to compare the elapsed time to the first event in my stack and fire it when needed. In this loop I would need to tell my views to update themselves and I know Apple guides state that UI updates must happen in the main thread.
I'm a little lost on how to conceptually achieve this kind of onEnterFrame loop in Cocoa as the paradigm is quite different to what I already know.
Of the many things I encountered, this gave me good timing accuracy in my initial tests (1/10th of a millisecond error):
func test() {
print("test")
}
// Run test() every 1 millisecond
let timer = NSTimer.scheduledTimerWithTimeInterval(0.001, target: self, selector: #selector(test), userInfo: nil, repeats: true)
I'm having difficulty removing 1 SKaction. I know that if I remove both SKactions with removeAllActions() but how do I go about only removing 1 will still having the other Action?
You need to use runAction withKey:
yourNode.runAction(someAction, withKey: "key")
// and then sometime later...
yourNode.removeActionForKey("key")
Obviously it makes sense to define the keys somewhere a bit less flakey than as hard-coded key every time you use them...
At the beginning of my scene I'm running a bunch of different actions as a sort of intro to the level. I would like to make it skippable by the user. Is there a way to handle this with SKAction? SKAction is my bet, but perhaps there's a better way to do this. Any ideas?
This assumes you want tap to stop the actions:
A very easy but possibly unstable approach would be to just crank the speed of the Node the action is running on to an insanely high number, just remember to reset it if you need the node.
An easy approach that is more stable would be to save your actions and reapply them with a duration of zero.
The best way to do this is to create a Dictionary for your SKActions like so:
var actionDict = [String,SKAction]()
Then just save all your actions with keys so that you can access it in the future:
actionDict["moveUp"] = SKAction.moveToY(100,duration:100)
Now that you have keys for it, when running your actions, just assign the same key:
node.runAction(actionDict["moveUp"],withKey:"moveUp")
Then when you need to end it, just remove the desired actions:
node.removeActionForKey("moveUp")
And reapply with a duration of 0:
//if we want to retain the old duration, use copy
let action = actionDict["moveUp"].copy()
action.duration = 0
node.runAction(action,withKey:"moveUp")
Note, this method only works where the action is doing a To event, if it is doing a By event, you need to figure out the final destination point, and make it a moveTo a with duration of 0
I'm creating a game with XNA 4.0 and I have a problem with resetting the game time in my game,
the problem is I'm using this code for adding my objects in game:
Timespan prevSpawn = timespan.zero;
Timespan objectSpawnTime = timespan.fromsec(5);
if (gameTime.TotalGameTime - prevSpawn> objectSpawnTime)
{
prevSpawn = gameTime.TotalGameTime;
AddObject();
}
I want to reset the game time to zero, when start the game again or game end and goes to mainmenu and hit the play again, I want to reset gametime.
As i understand from the MSDN entry below, the GameTime.TotalGameTime property will continue 'ticking' until you end the game, or, entire application.
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime.totalgametime.aspx
Perhaps it would be wiser to use your own object, for which you can reset with any of your actions and hence have more control over the time span. Then you could still use this property for counting the difference from one and the other.
i.e. when you reset/restart etc, do not refer to TotalGameTime and try to make it be zero, but refer to your own object's timespan, or to (TotalGameTime - TimeWhenReset) where TimeWhenReset is the timespan copied from TotalGameTime when you reset.
This is untested, but I think that it should work if I understood correctly.
For a more accurate timer, you would be better using a time span, and a DateTime vairable. Set the DateTime to now when you want to start the timer, and in the TimeSpan do something like this:
timer = StartTime - DateTime.Now;
This will give you a timer you can deploy at any time.
I'm trying to achieve high frame-per-second on Windows GDI by using Windows Timer Queues. The relevant APIs are CreateTimerQueue, DeleteTimerQueueEx, CreateTimerQueueTimer, and DeleteTimerQueueTimer .
The timer is created using CreateTimerQueueTimer(&m_timer, m_timer_queue, TimerCallback, this, 0, 20, WT_EXECUTEINTIMERTHREAD); to achieve some 50fps of speed. GDI operations (some painting in the backstore, plus InvalidateRect) cannot be asynchronous, therefore I can't choose other flags but WT_EXECUTEINTIMERTHREAD so that no extra sync op is required on the drawing code. The idea is to achieve 50fps when possible, and when it's not, just show each frame at the maximum possible speed.
At the end of the animation (reached a total frame count), DeleteTimerQueueTimer is called to destroy the timer.
The problem is that DeleteTimerQueueTimer doesn't immediately turn off the callings of the callback function. When it's not possible to achieve the 50fps requirement, the timer pumps the call into a queue. Calling DeleteTimerQueueTimer inside the callback function doesn't destroy the queue. As a result, the callback is still being called even though it decided to shutdown the timer.
How do I deal with this problem?
-
On another note, the old timeSetEvent / timeKillEvent multimedia API doesn't seem to have this problem. There are no queues and the calling of the callback function is immediately stopped when I call timeKillEvent. Is it possible to achieve the same behavior with timer queues?
You can pass the WT_EXECUTEONLYONCE flag to the CreateTimerQueueTimer function. This will cause the timer to trigger only once and not periodically.
You can then reschedule the timer with the ChangeTimerQueueTimer method.
To cover the times where your drawing takes too long too complete in the frame, you can add a CriticalSection to the beginning of the TimerHandler method, which will cause the 2nd timer to wait until the first one completes.
If you want to run something at 50fps+, you'd probably do better to actually just have a draw loop which computes the amount of time between frames and scales the animation accordingly. Timers aren't really meant to fire so often. So (and this would probably be in your Idle handler). Like, this pseudocode (ignore lack of error handling):
static longlong last_frame;
while(1) {
longlong current_frame = QueryPerformanceCounter();
long delta = current_frame - last_frame;
// Do drawing here, scale amount to move by how much time has elapsed
last_frame = current_frame;
}
DeleteTimerQueueTimer will cancel the timer provided it has not already been scheduled. (When you use WT_EXECUTEINTIMERTHREAD I believe they are queued as an APC on a thread from a thread pool shared by the timer queues and worker threads. ) If it has already been scheduled (not just running) - it will be run and the DeleteTimerQueueTimer call will block until completion.
If I understand your problem correctly, may I suggest the following?
1. Before calling DeleteTimerQueueTimer - set a flag say abortAllTimers to true.
2. In each timer call back function check to see if abortAllTimers is true. If it is true - then return at once without doing any drawing.
And finally - DeleteTimerQueueTimer should not be called from the timer callback. Instead I would suggest you should call it from any other thread - say the thread you used to start the timers.
Hope this helps.