I have two NSMutableArrays, collectables and collectableViews.
My app consists of a character moving around and gathering collectables (coins, apples, bananas, etc) for points...
I would like the collectables to disappear after a certain amount of time.. however, I am confused as to where to put an NSTimer to not disrupt Model/View/Controller design.
For example, If I put an individual timer in each model, the model doesn't know of the view and cannot remove the view..
If I put the NSTimer in the controller, I would need to make another array consisting of all the collectables on the screen, in order of which one expires first. The timer's method would fire every second and remove each collectable when they are due.
Is there an easier, better way to do this?
Most games model this kind of 'state-monitoring' using one or more game clocks. You can do something like this:
Create a data structure containing a duration time, function
pointer, and array of object variables. For this example let's call
it DecayEvent.
Create a static, mutable array of DecayEvent's in your front (main)
controller, with some nice accessor methods
Choose an appropriate event processing interval. It needs to be
large enough to process what you think the maximum number of events
will be, but small enough not to retard user experience.
Create a method on your front controller that will process through
the array of decay events. Every time the method is called it will
iterate the array and decrement the event's duration time by the
event processing interval. If the decay events duration falls below
zero then 'fire the event' (basically, trigger its callback function
in another thread, with the callback arguments).
Create an NSTimer in your main thread. Set it to call your
processing method at every event processing interval.
You will have to tweak quite a bit to get everything working the way you want, but the steps above will generally work.
Good luck!
Your current situation tends that you should keep timer in your controller because controller has access to each data modal and views you can access arrays too. also another approach is to use NSNotificationCenter. First try and if that doesn't work then let us know.
Related
I’ve added multiple instances of a scene as enemies, and now I want to remove one of them when it dies. I tried using “queue_free()” on it, but this only works for one instance, and every instance afterwards doesn’t get removed and returns a “node not found” error. How do I just remove one instance at a time?
Your question can't be easily answered because we don't know your specific code and there are multiple good ways to do this. As far as I understand you, you have an enemy class with some kind of health level. You could create a function that checks if the health is above zero or some other variable like alive. A very easy way would be like
func is_alive():
if health > 0:
return True
else:
queue_free()
You could call this function in every process cycle and also add some dying animation or counter later.
I was using performSelector to generate SKSpriteNodes after a random period of time, until a lot of people told me it was better to use dispatch_after.
What I have understood is that I need to create a function that will have the dispatch_after and then there it will point to another function that I want to do repeatedly. But this creates a problem to me. I need to pass two arguments to the method that creates the SKSpriteNodes from another class.
If I've understood correctly, in that class I need to call the method that has the dispatch_after so that it can perform the action repeatedly. Then, how would I do all of this? Do I need to pass those arguments multiple times? Also, I cannot addChild:the method that has the dispatch_after because it does not return a SKSpriteNode (the one that creates them does indeed). So, I'm really confused.
I am currently making a application using NSTimer as base for recording time. However, in the event that the player used a hint or forfeit on the current question, I would like to have a penalty of increasing time to the on-going NSTimer.
The question I have is, is it possible to add in more value to the on-going NSTimer?
e.g The timer is at 0:23:41.2, and the user press hint/forfeit. I want to have a function that add, lets say 10min. How do I make a function such that the timer will become 0:33:41.2 when the player press hint/forfeit?
I tried finding on google and SO on articles related to NSTimer and came across this SO thread which might be useful. How to value for NSTimer
Instead of elapse time as shown in the thread, I think I could swap it with a variable like below. (But if the example I shown below is not feasible, please enlighten me. I really want to make this game a successful one)
-(BOOL)hintButtonPressed
.
-(void) timerFireMethod:(NSTimer *) theTimer {
if (hintButtonPressed == true) {
// do something to increase value
[theTimer invalidate];
}
}
Even though I would not use a NSTimer for a countdown, there is an easy way to add time to its fireDate:
[_timer setFireDate:[[_timer fireDate] dateByAddingTimeInterval:600]];
This would add 10 minutes (600 seconds) to my _timer.
And the best solution for your example would be to create an action-method for the button and check in that action-method whether the user already got a time bonus.
Of course you can modify the fireDate in the fire method but it won't fire again with my codeline.
NSTimer is not a good tool for tracking time. It's a good tool for scheduling periodic actions (particularly actions that don't have very tight timing requirements).
The way you track time is with three ivars: the most recent "timer start" time, the "accumulated time" (which is the time prior to the most recent start), and an "isRunning" boolean. At any given time, the total time is:
accumulated + (now - start) * (isRunning ? 1 : 0)
With this, you can easily add or subtract time by modifying accumulated. And you don't need an NSTimer to update your data (which is seldom a good approach).
To what i remember, you can't add, increment time to a NSTimer, you need to create a new one, and release / invalidate the older one.
By the way, if it's a simple timer, you still can make it easy
[NSObject cancelPreviousPerformRequestsWithTarget: self]
[self performSelector:<#(SEL)#> withObject:<#(id)#> afterDelay: incrementedTime];
But you if you performSelector, you cant get precise time information like NSTimer, you can just blind increment time. So all depend of your needs.
I have an array of NSDictionaries and a NSDictionary iVar (*selectedDictionary) that points to one of the array's objects. *selectedDictionary points to a different object every time the user selects a different row in a NSTableView. Several GUI controls are binded to the selectedDictionary instance (IB).
I just want to make the NSDocument dirty (edited) every time the user alters the above controls. I think using Key Value Observing for ALL the objects in the array and all their kaypaths, is a bit insufficient. Any suggestions?
Thanks
NSDocument's support for marking a document as dirty comes directly from the NSUndoManager. The easiest way to change the document to dirty is to do an implementation of Undo, and this is basically going to mean doing the undo inside of the model class that the document is using (or the subclass of NSDocument if you choose to handle all storage directly in there).
Apple has documentation on this here:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/UndoArchitecture/Articles/AppKitUndo.html
Since you indicate you have an array of dictionaries, that's going to make it a bit more work to implement, but once you have nailed that, you'll be in good shape.
Alternatively, if you don't want to go with the freebie support provided by NSDocument and NSUndoManager, you can manually handle undo and use the updateChangeCount: method to modify the internal understanding of whether changes have occurred. This takes some work, and likely is a lot less useful than just setting up undo correctly.
As for the efficiency of observing all the objects in the array, I wouldn't worry about it unless you have profiled it and found it to be inefficient. KVO is really pretty darned efficient and we regularly observe multiple values in every element of arrays without seeing performance problems. You have to observe the array itself in order to handle adds and removes (assuming your arrays have this).
As far as I can tell, though, you have a selectedDictionary which is used to determine the other controls that are shown. In this case, you can use KVO to observe the value of selectedDictionary and when it changes, you can remove the observers from the previous selectedDictionary and add them to the keys in the current selectedDictionary. This is basically what bindings is doing in order to handle the display and setting, anyway.
One other consideration that I've used in the past is referenced in this StackOverflow post:
NSMutableDictionary KVO. If you look at my answer here, I outline a trick for getting notifications when a new key is added or an existing key is deleted. It also has the benefit of giving you a notification when there's any change. It's not always a great solution, but it does save some effort on coding the list of keys to observe.
Beyond that, you'll have to add every key you're expecting to have an effect on the saved state of the document.
I want to use the property currentPlaybackTime, which returns a double value to trigger a series of if statements at certain points throughout the movie's playback to pause it. Because of the approximate behavior of doubles, it never syncs up with the value in my if statement, and the movie doesn't stop. I can't use a range though because when I resume playing, it might still be within the range an immediately pause again.
SO I think what I need to do is round this double value to 2 decimal places.
But how do I observe each change to the currentPlaybackTime property? Right now I have a timer set to call the method checking my if statements against the playback time every .0333 seconds(approx 30 times a second(my movies framerate)).
However this is inexact and I feel almost certainly wrong.
Perfect time to check out Key-Value Observing.
Just an FYI for others since this is an old thread:
unfortunately currentPlaybackTime doesn't seem to be KVO-compliant, so Hippocrates method of polling with a timer is probably the best alternative
I'm not sure to what the property you're attempting to observe belongs, but it must be KVO compliant for this approach to work. That isn't a given for every property.
Internally, the code providing you currentPlaybackTime must call willChangeValueForKey: and didChangeValueForKey: (if it uses KVC to change the property, it gets these calls for free). I'd check the documentation (or the code if it's yours) to ensure this is observable.