I'm trying to write a little app, where on the main screen, I animate a flying "bubble". This animation has to be continuous. (I reuse the bubbles, which fly off the screen) I heard that animations have to run on the main thread, as does every operation which changes the UI. Is this true? When I try to show a UIAlertView on this screen, it's animation becomes very discursive because of the continuous bubble animation. (this is a custom alertview with an indicator) The device is an iPhone 4, so I don't think it should be a problem to show a normal UIAlertView.
And I would like to ask if I use the correct method for the bubble animation. So first of all, I use an NSTimer, which invokes the startAnimation method in every 0.01 seconds (I start it in the controller's viewDidAppear: method). In the startAnimation method, at first I generate bubbles with random x and y coordinates (to see bubbles on the screen right after the viewdidappear), and I generate bubbles on the bottom with random x and y = 460 coordinates. In the startAnimation method, I run a counter (called frames), and when the value of this counter equals 35, I call the bubble generate method again.
The problem:
I store the generated bubbles in an array, and the 'gone' bubbles (which are off the screen) in another array. First I try to reuse the bubbles in the gonebubbles array, then if the array is run out, I generate new bubbles. While this operation is processed, the continuous animation stops, then continues. The break is about one second, but this is very disturbing.
Can anyone help in this problem? Thanks in advice, madik
- (void)viewDidAppear {
.
timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(startAnimation) userInfo:nil repeats:YES];
.
}
- (void)startAnimation {
self.current = [NSDate timeIntervalSinceReferenceDate];
double diff = (self.start - self.current);
if ( diff < 0 ) {
diff = (-1) * diff;
}
self.start = self.current;
frames++;
if ( shouldMoveBubbles ) {
[mug moveBubbles:diff];
}
if ( frames == 35 ) {
DebugLog(#"################################################################");
DebugLog(#"####################### FRAME = 35 ###########################");
DebugLog(#"################################################################");
[mug createNewBubbleOnTheBottomOfView:self.view];
frames = 0;
}
}
In the Mug class:
- (void)moveBubbles:(double)millisElapsed {
for (Bubble *bubble in bubbles) {
int bubbleSpeed = bubble.speed;
float deltaX = (float)(bubbleSpeed * -degrees_sinus * millisElapsed * 100);
float deltaY = (float)(bubbleSpeed * -degrees_cosinus * millisElapsed);
DebugLog(#"movebubbles x: %f, y:%f, speed: %d, sin:%f, cos:%f", deltaX, deltaY, bubbleSpeed, degrees_sinus, degrees_cosinus);
[bubble moveBubbleX:deltaX Y:deltaY];
}
}
And in the Bubble class:
- (void)moveBubbleX:(float)deltaX Y:(float)deltaY {
self.bubbleImage.center = CGPointMake(self.bubbleImage.center.x + deltaX, self.bubbleImage.center.y + deltaY);
}
This sounds like a memory problem. Slow UIAlertView animation is a sure sign of this. It sounds like the way you are generating bubbles is causing the problem. You mentioned the you keep two arrays of bubbles. You never say if you limit the number of bubbles that can be in either array at once. You also don't mention when you clean up these bubbles. It sounds like a memory "black hole". I'd recommend setting a maximum number of bubbles that you can show on screen at once.
Also, you mention a custom alert view. If you're modifying the UIAlertView, you're going to run into problems since that's not officially supported. Additionally, I've seen UIAlertView animation become slow when memory is tight. If you solve the memory issues with your bubbles, you'll probably solve this one too.
Finally, a word of advice. Making an animated game in UIKit is probably not a good idea. NSTimers are not as accurate as many people would like to think. UIImages are relatively expensive to load. Touching moving buttons is known to be unreliable at worst, hackish at best. I suggest looking into a game framework, such as Cocos2d-iphone.
Good luck!
Related
I am using tilemaps and animated tiles from the 2dExtras in unity.
My tiles have 6 frames, at speed=2f, and my tilemap frame rate is 2.
New tiles placed always start on frame 1 and then immediately jump to the current frame of the other tiles already placed, the tilemap is keeping every tile at the same pace, which is working as I want.
However I would like the newly placed tiles to start at the frame the others are currently on,(instead of placing a tile that jumps from frame 1 to frame 4) I would like the new tile to start on frame 4
I've found how to pick the frame I want to start on, however I am having trouble retrieving which frame the animation is currently on, so I was wondering how exactly can I access the current frame of animation of a given tilemap ( Or a given tile, I can create a dummy tile and just read the info out of it, how can I get the current frame of an animated tile? )
The animated tilemaps feature seems to lack the feature to retrieve this information, also when I try tilemap.getsprite it always returns the first frame of the sequence(does not return the sprite currently displayed), and there doesn't seem to be any method to poll info from tilemap.animationFrameRate.
I thought another method would be to set a clock and sync it to the rate of the animation but since I can't get the exact framerate duration the clock eventually goes out of sync.
Any help would be appreciated!
I found a way to solve this question. But it's not 100% insurance.
First of all, I used SuperTile2Unity. That doesn't seem to be the point.
private void LateUpdate()
{
// I use this variable to monitor the run time of the game
this.totalTime += Time.deltaTime;
}
private void func()
{
// ...
TileBase[] currentTiles = tilemap.GetTilesBlock(new BoundsInt(new Vector3Int(0, 0, 0), new Vector3Int(x, y, 1)));
Dictionary<string, Sprite> tempTiles = new Dictionary<string, Sprite>();
//I use SuperTiled2Unity. But it doesn't matter, the point is to find animated tile
foreach (SuperTiled2Unity.SuperTile tile in currentTiles)
{
if (tile == null)
{
continue;
}
if (tile.m_AnimationSprites.Length > 1 && !tempTiles.ContainsKey(tile.name))
{
// find animated tile current frame
// You can easily find that the way SuperTile2Unity is used to process animation is to generate a sprite array based on the time of each frame set by Tiled animation and the value of AnimationFrameRate parameter.
// The length of array is always n times of AnimationFrameRate. You can debug to find this.
tempTiles.Add(tile.name, tile.m_AnimationSprites[GetProbablyFrameIndex(tile.m_AnimationSprites.Length)]);
}
}
//...
}
private int GetProbablyFrameIndex(int totalFrame)
{
//According to the total running time and the total length of tile animation and AnimationFrameRate, the approximate frame index can be deduced.
int overFrameTime = (int)(totalTime * animationFrameRate);
return overFrameTime % totalFrame;
}
I have done some tests. At least in 30 minutes, there will be no deviation in animations, but there may be a critical value. If the critical time is exceeded, there may be errors. It depends on the size of AnimationFrameRate and the accumulation mode of totalTime. After all, we don't know when and how the unity deals with animatedTile.
You could try using implementation presented in [1] which looks as follows:
MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.length * (MyAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1) * MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.frameRate;
[1] https://gamedev.stackexchange.com/questions/165289/how-to-fetch-a-frame-number-from-animation-clip
I wrote a function which, on zoomstart, (should) do_something1 if I zoomIn and do_something2 when I zoomOut:
'var prevzoom = map.getZoom;
map.on('zoomstart', do_something);
function do_something(e){
var currzoom = map.getZoom();
var diff = currzoom - prevzoom;
if(diff > 0){do_something1}
else {do_something2}
prevzoom = currzoom;'
It behaves as follows:
1)when loading, if I zoomOut it do_something2 (and the second, third... time as well
2)when loading if I zoomIn it do_something2 (uncorrectly) but it do_something1 the second, third... time and so on...
3) when I change from ZoomIn to ZoomOut and viceversa the first time it behaves as the previous time and it behaves correctly on the second, third...time when zooming in the same way (i.e. In or Out).
I read in the post "Detecting if is zoom-in or zoom-out with leaflet" its it not possible to know if zoomIn or Out when the event zoomstart is triggered.
In my case it seems to work most of the time (which is not good anyway).
What I am missing/did wrong?
I have no knowledge in coding: thank you for help.
Giuseppe
I'm trying to fade out 2 sounds at the same time in the piano app on this page:
http://www.intmath.com/trigonometric-graphs/music-note-frequencies-interactive.php
I'm using the "voices" approach and the exponential fadeout is working fine in single-note mode;
vca.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 1);
oscillator.stop(audioContext.currentTime + 1);
However, after choosing "Combined signal: yes" (where you hear a 220 Hz note plus the note played), the above will fade out the non-220 Hz note correctly, but the fundamental A-220 plays at full volume for the whole 1-second duration after key up. It's ugly, especially on small speakers.
I've disabled the fadeout for now in the "combined" case (both sounds just stop immediately, which is better than having one not fade at all.)
This is the function called on key up:
function doUp(thisKey, e) {
if(audioContext) {
for(i=-1; i<18; i++) {
if( typeof(active_voices['key'+i]) != "undefined") {
active_voices['key'+i].stop();
delete active_voices['key'+i];
}
}
}
audioOn = false;
}
I have tried changing the first line of the middle "if" to the following, but it didn't improve things:
active_voices['key'+i].stop(audioContext.currentTime + 1);
How to fade out 2 voices at the same time?
TIA.
Alright I'm offically stumped. I've been able to figure out my problems myself in the past but I'm lost on this one.
I'm trying to flip a child sprite's xscale. I want the flip to occur when the child's x position is negative. With the code below, the sprite does flip, every time the sprite reaches negative position, it loops back and forth flipping until reaching positive again.
I've tried numerous variations, but this is the code in it's simplest form. Any help or alternatives would be appreciative.
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let pin = childNodeWithName(Pin1) as SKSpriteNode!
let guyPoint = guy.convertPoint(guy.position, fromNode: pin)
if guyPoint.x <= 0 {
guy.xScale = -1
}
else {
guy.xScale = 1
}
}
So if it is constantly flipping it's probably an issue of coordinate space. So check to make sure your convertPoint method is accurately returning the point you want. I would use println() to see what's going on there.
Probably what's happening is you want to flip the xScale when the child node is negative in the scene space or something (i.e. when the child is off the screen) but instead guyPoint might be the position of the child in its parent's coordinate space (so relative to the parent).
Also try with and without the else{} part to see if that changes anything.
I'm trying to make a game where the user is supposed to drag a sprite up and down on the screen, avoiding incoming obstacles. The last answer here helped me to drag the sprite around on the screen, but I want to set a maximum speed the sprite can be moved (and hopefully with a natural-looking acceleration/deceleration), so it doesn't get too easy to avoid the objects.
Does anybody know how I can modify the code to achieve this, or is there another way to to it?
Thanks :)
You'll need to maintain a CGPoint destinationPosition variable which is the location of your finger and use an update loop to modify it's position:
-(void) update:(ccTime) dt
{
CGPoint currentPosition = draggableObject.position.x;
if (destination.x != currentPosition.x)
{
currentPosition.x += (destination.x - currentPosition.x) / 5.0f; // This 5.0f is how fast you want the object to move to it's destination
}
if (destination.y != currentPosition.y)
{
currentPosition.y += (destination.y - currentPosition.y) / 5.0f;
}
draggableObject.postion = currentPosition;
}
In the ifs, you might want to check if the objects are close to each other, rather than exactly the same number to allow for rounding errors.
You just need to have an if statement in whatever schedule updater you are using, like time, or touches, or whatever.
I'm presuming you have x/y velocities? Just inside your update statement, wherever your acceleration is -
if(acceleration.x > 20){
acceleration.x = 20;
}
if(acceleration.y > 20){
acceleration.y = 20;
}