In short:
Is there a better way to run large functions for a long time that's less 'lagy' and memory abusive than putting it all in the update function?
Full question
I'm currently in the final stages of developing my first game app using iOS Swift and SpriteKit. One of the bigger problems I got is lag/delay. The Time Profiler Instrument pointed out that my override func update was using a lot of memory and time. My update function consists of the following:
override func update(currentTime: CFTimeInterval) {
if hasStarted {
// Character
updateCharacterPosition()
updateJumpMotion()
// Blocks
blockRunnerDebug()
// Wave
debugRunningBarPosition()
// Game Engine
gameEngine()
debugGameEngine()
}
// Update Scenery
updateScenery()
}
As you can see it has a lot of functions. Most of them run background animations like water, clouds or the jumping animation of the character.
Example:
// Move y
self.cloud01.position.y = self.cloud01.position.y + (CGFloat(createSinWave(0.5, b: b, angle: angle))) * 0.3
self.cloud02.position.y = self.cloud02.position.y + (CGFloat(createSinWave(0.5, b: b, angle: angle))) * 0.3
My question is: is there a better way to run large functions for a long time that's less 'lagy' and memory abusive than putting it all in the update function?
Thanks
Items that may change direction or position based on user interaction need to be in update. There is no way around that.
For items like background animations you can (and should) set them up once at the beginning and put them into an infinite animation loop instead of manually updating the position every update. That should help a lot.
Related
So here's the thing:
I'm writing a demo for genetic algorithm. The demo is about using polygons to approximate an image. The process of the algorithm is like evolving generation by generation and I want to display every generation so it'll show how these polygons are evolving.
So I have a GAView: UIView class, it has a property called currentDrawing which means the current generation of polygons.
I use BezierPath to draw those polygons.
I have a function evolve which gets a snapshot image from the current view and calculate the pixel errors of the image and then update the view according to the pixel errors.
So the process is like this:
Draw the view -> Snapshot the View -> Do Calculations based on the Snapshot -> Change the view's property and redraw it -> Loops Back
The evolve is like this:
func evolve() {
let fooDrawing = currentDrawing.clone()
fooDrawing.mutate()
if fooDrawing.isDirty {
generation += 1
//This redraws the GAView because I called setNeedsDisplay in the didSet of currentDrawing
GAView.currentDrawing = fooDrawing
let newErrorLevel = FitnessCalculator.calculateFitnessFor(currentDrawingView: GAView)
if newErrorLevel <= errorLevel {
objc_sync_enter(currentDrawing)
currentDrawing = fooDrawing
objc_sync_exit(currentDrawing)
errorLevel = newErrorLevel
}
}
}
So calling evolve() has a serious problem that the calculations is so fast and the UIView's drawing is actually not in real-time which caused the UIView to wait forever because every time it wants to redraw its currentDrawing changes. And my calculation cannot get the correct View to calculate. And iOS keeps caching those snapshots which eventually crashed Xcode.
So I'm wondering how I can make sure the calculations are executed after the view is drawn?
I'm making a clone for that old game Simon (a.k.a. Genius, in Brazil), the one with the for coloured buttons which the player needs to press following a sequence of colors.
For testing, interface has 4 coloured buttons
I created an array for the button outlets, for easy access:
var buttonArray:[UIButton] = [self.greenButton, self.yellowButton, self.redButton, self.blueButton]
Also, created another array to store the sequence of colors
var colors:[Int] = []
When a game starts it calls a function which adds a random number from 0 to 3 (index on buttonArray), and add this number to the colors array
After adding a new color the color sequence, the app needs to show the sequence for the user, so he can repeat it
For that, it calls the playMoves function, which uses a for loop the run through the colors array and change the alpha from the button, simulating a 'blink'
func playMoves(){
let delay = 0.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
for i in self.colors{
self.buttonArray[i].alpha = 0.2
dispatch_after(time, dispatch_get_main_queue(), {
self.buttonArray[i].alpha = 1
})
}
}
It changes the alpha from the button to 0.2 and then, after half a second it returns the alpha to 1. I was using dispatch_after, passing the 0.5 seconds and on the code block it returns the alpha, as you guys can see on the code above.
On the first run, it appears to do it correctly, but when the colors array has 2 or more items, when it runs the loop, although it has a 0.5 sec delay, it blinks all the buttons on the same time.
It's probably some dumb mistake I'm making, but I'm clueless on the moment.
I would appreciate very much all the help!
Thanks!
All of these dispatch_after calls are scheduled at nearly the same time, making them appear to blink at the same time. There are a couple of approaches that would solve this:
You could, for example, adjust the when parameter (the dispatch_time_t parameter) for each button to be offset from the original time (such that delay was, effectively i * 0.5 * Double(NSEC_PER_SEC)).
You could also use key-frame animation, but I'd suggest you first try fixing the delay in your dispatch_after approach first.
Good day everyone.
I'm trying to draw the text "Pass Complete!" to screen with this code:
spriteBatch.DrawString(font, "PASS COMPLETE!", new Vector2(30, 130), Color.White);
Which does fire off the proper IF statement. However, how do I go about then removing that text from the screen? I'm really not sure at all where to go on from here and my instructor wants me to google the answer or find it in textbook. I have been all over my XNA textbook and I have found no outlet to removing that text.
Thanks for any and all help.
Update:
protected override void Draw(GameTime gameTime)
I have the IF statement included in here. Basically it checks collision with p_Receiver and if it the bool checks out, it draws the DrawString. Should I maybe be looking at this from a different angle?
Final:
I went ahead with the following as the answer and it's working better then before. :)
if (PassInfo == 3) {
(timer code)
(IF timer not "used up" then run the draw)
Working good for now.
I appreciate it.
I'm doing this by function that add text with some parameters into generic list. and then i update and draw items from that list. in pseudo code:
function addText(text,position,duration)
texts.add(new t(text,position,duration))
end function
function updateText()
for each t as text in texts.findall(where t.active)
t.duration -= 1
if t.duration < 0 then t.active = false
next
end function
function drawText()
for each t as text in texts.findall(where t.active)
//draw it
next
end function
so by this you can add unlimited number of texts on different position and duration on screen.
A lot of games redraw the entire window / screen each time through the draw cycle so there's a distinct chance that the solution to removing it is simply to stop drawing it.
i.e. have your if condition not draw the text when it is no longer required.
If, on the other hand, you've some more complex drawing logic that only draws portions of the window / screen that need updating then you'll need to include logic to redraw that part of the screen that contained the text once it is no longer needed.
I'm wondering why the following thing does not work correctly.
Before the nodes are drawn, I analyze if two specific nodes intersect by using:
[[self playerSpriteNode] intersectsNode: [self pSKLabelNode]]
When pSKLabelNode touches desiredSpriteNode it works perfect! (By returning true, or false when it doesn't intersect)
But when it "passes" by a few pixels away from the SKLabel it still intersects and returns true.
Is there some setup that are recommended to fix the frame size of the nodes, or solutions that you think that will fix the problem?
I have the same problem when I try to intersects static node (that located left) with node that have rotation (and flying from the right side). I fix it like this
if ([ninja intersectsNode:node] &&
CGRectGetMinX(node.frame) <= CGRectGetMaxX(ninja.frame) &&
CGRectGetMaxX(node.frame) >= CGRectGetMinX(ninja.frame))
{
//and here I have intersects
}
So I fix it by adding additional parameters
The intersectNode method is optimized for running fast on devices with lots of iterations per second. Due to this, it actually "estimates" collision based on math, which sometimes goes wrong at a margin of a few pixels, specially when we are speaking of square corners of PNGs.
I had this problem once too, and since i used circles I calculated distance between circles as a second verification.
So, what you can do is a custom verification INSIDE the intersectsNode if case. Assuming you handle squares, you could verify wether the x or y collides after the intersectNode. It could be something like like:
if([[self playerSpriteNode] intersectsNode: [self pSKLabelNode]]){
if(distance between x1 and x2 < size1.width/2 + size2.width/2 || distance between y1 y2 < size1.height/2 + size2.height/2){
//Your code goes here
}
}
Note that we compare central x distances with half each widths summed. This is only an example that works with squares, and most generic sprites.
I would like to point out that, while intersectsNode is slightly imprecise, this is NEEDED in order to run your game swiftly, as perfect and precise calculations per update can be very exhaustive to your device.
So, should you do a custom verification, ALWAYS call it after intersectsNode returns true, as a second verification rather than the only one.
The answer provided above by Roman pretty much does what i said, in shorter code; I just wanted to leave an explanation about why.
Swift 2.1:
For increased results do your check in the update( _:) loop, is where things happen before the next drawing:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if goodDude.intersectsNode(badDude){
print("bad dude v.s. Ninja")
}else{
print("not dude at all")
}
}
I'm making an endless running game (e.g. canabalt, temple run, Jetpack Joyride) and I'm trying to get the "feel" of it right. So far, I'm using the following equation to set the speed:
speed = (time+500)*(.05+(time/300))
Any tips for how to make the increase feel just right, other than trial and error?
Well, I did something similar in one of my games but I did not increase speed constantly, I increased it once every minute or once the player reaches a certain amount of points. Like so:
- (void)setTravelTimeTo:(NSNumber*)targetTime
{
if (maxTravelTime > targetTime.floatValue)
{
maxTravelTime -= 0.1f;
[self performSelector:#selector(setTravelTimeTo:) withObject:targetTime afterDelay:2];
}
}
Where maxTravelTime is the time or in your case speed. Just modify it to suit your needs. The travel time in this case was the time a moving platform needed to get across the whole screen.
Hope it helps.
Generally you are going to accumulate the speed and position as you go. So something like
a = <some function of current speed (drag), player actions, and terrain>
v = v + a*deltaTime
x = x + v*deltaTime
DeltaTime is just the time since the last computation - possibly the last frame. An implication of this is that v should be at most linear with time (not quadratic as in your formula). Position is at most quadratic. The computation for "a" should ensure that as v approaches some maximum speed (possibly level dependent), "a" goes to zero.