Execute code continuously in framer - coffeescript

I have a little question about framer. Currently I have a little privat project in which I need to run different actions when the mouse is pressed or not.
To make it easier to talk about, I set up a simple scene with a rectangle. Now I want that the rectangle rotates to the right when the mouse is pressed and rotates to the left when the mouse isn't pressed.
layer = new Layer
x: Align.center
y: Align.center
layer.onMouseDown ->
layer.rotation += 1
layer.onMouseUp ->
layer.rotation -= 1
The problem is that the code only checks once if the mouse is pressed or not. I don't know how I let the program check continuously if the button is pressed or not.
Could anyone help me please?

If you want something to happen repeatedly without events being fired (because mouseUp and mouseDown only get fired once per mouseclick), you can use intervals. Here's an example that does what you describe:
intervalDown = null
intervalUp = null
layer.onMouseDown ->
window.clearInterval(intervalUp)
intervalDown = Utils.interval 0.1, ->
layer.rotation += 1
layer.onMouseUp ->
window.clearInterval(intervalDown)
intervalUp = Utils.interval 0.1, ->
layer.rotation -= 1
You need to create an interval on every event and clear it again using window.clearInterval when the other event fires.
Here's the full example: https://framer.cloud/SEYHy

Related

Adding SKShapeNodes - using while loop blanks UI

I have a game which is running well but as soon as I introduce a while loop my entire UI goes blank.
I have some code which generates a sprite from an array and moves it down the screen
func addALetter() {
let randomX = CGFloat.random(in: 50...size.width - 50)
let shrink = SKAction.scale(to: 0.1, duration: 0.01)
let grow = SKAction.scale(to: 1, duration: 0.5)
let wait = SKAction.wait(forDuration: 0.7)
let spawn = SKAction.move(to: CGPoint(x: randomX, y: size.height - tileSize), duration: 0.001)
let move = SKAction.moveTo(y: -500, duration: 7)
let sequence = SKAction.sequence([shrink, spawn, grow, move, wait])
// scroll through the lettersArray
if activeLetter < lettersArray.count - 1 {
bubbles[activeLetter].removeAllActions()
bubbles[activeLetter].run(sequence)
activeLetter += 1
} else {
// go back to first in letter array
activeLetter = 0
bubbles[activeLetter].removeAllActions()
bubbles[activeLetter].run(sequence)
}
}
It is working fine triggered using an SKAction in my didMove to view run(SKAction.repeatForever(SKAction.sequence([SKAction.run(addALetter), SKAction.wait(forDuration: spawnTime)])))
but I have problems with that as I get to the end of the array because the action repeats too frequently making sprites disappear before I want them too.
So I tried using a while loop instead...
while gameOn == true {
addALetter()
}
to repeat the action. But then I get a completely blank UI - I assume because it's then stuck in the loop there's no time to update the UI?
Looking for a solid way to repeat the function that I can vary the frequency as the array gets to low numbers
It seems likely that the problem is that spawnTime is small enough that you're wrapping around through the letters too quickly and start doing bubbles[activeLetter].removeAllActions() before the bubble's previous action sequence has finished.
I think the most best way to deal with this sort of situation is to coordinate through completion blocks. I.e., use the run method with a closure to be called after the action finishes, https://developer.apple.com/documentation/spritekit/sknode/1483103-run. That way you don't wind up trying to adjust delays explicitly in an effort to keep actions from being prematurely cancelled. The completion blocks will update state that you can use to coordinate, or they can run other actions directly.
It's not clear from your question what behavior you want. But, e.g., you might have addALetter set a flag for the bubble when it starts the action sequence and include a completion block for the sequence to clear the flag. Then before addALetter restarts the sequence for a bubble it can make sure that the flag is clear; if it's not (the bubble is still running the previous sequence), just return without doing anything.

Force the SKAction.move to be completed - swift

I'm creating a Snake Game using swift and the library SpriteKit.
To control the direction of the snake, the user has to swipe on the screen. To check which was the direction of the swipe, I use UISwipeGestureRecognizer .
Now, I have to pass the information (direction of the swipe) from the GameViewController.swift file to the GameScene.swift file. To to that, I declare a 4 items array called movesConoller:
movesController[false, false, false, false]
If the user swipes up, the first element of the array turns true, if he scrolls down, the second element turns true and the first one false... etc. etc.
Now, in the GameScene.swift file I have to say what the Snake has to do if the player moves up.
For this reason, I use 2 more variables:
var playerX:CGFloat = 0
var playerY:CGFloat = 0
and then I created this code
if movesController[0] == true {
playerY += 56
}
if movesController[1] == true {
playerY -= 56
}
if movesController[2] == true {
playerX += 56
}
if movesController[3] == true {
playerX -= 56
}
Now I create the movement
let startPoint = CGPoint(x: player.position.x, y: player.position.y )
let endPoint = CGPoint(x: playerX + player.position.x, y: playerY + player.position.y)
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt)
So, every time the program is executed, the playerX and playerYvariables become equal to 0. Then, based on the direction of the swipe, they are added or subtracted 56.
To move the snake I just say to the head to move from his startPointto his endPoint in 0.1 seconds.
Another thing I added is the tail. To create it I use two arrays, snakeX and snakeY.
To these two empty arrays (of type CGFloat) I add the previous position of the snake and then, if the score remains the same, the last element of each array is deleted. Else, the last element isn't deleted and remains in his array.
Whit this method, I make the tail growing when the snake eats an apple.
BUT the head moves 56 in 0.1seconds. This means that he makes a movement of 8 pixels at each execution. Because of that, I have to add to snakeX and snakeY the X and Y values every 7 execution of the program.
This is the problem. If the player swipes on the screen just after 7 execution of the program, the movement of the tail will be perfect. But if the player swipes before the 7 execution, there will be a problem. Let's suppose the snake is moving right and the player swipes up when the program is at its 4th execution.
//snakeX and snakeY values before the swipe
snakeX[168, 112, 56, 0] //all this numbers are multiple of 56
snakeY[224, 224, 224, 224] //the snake is moving right, the Y value doesn't change.
//snakeX and snakeY values after the swipe
snakeX[168 ,112 ,56 , 0]
snakeY[248, 224, 224, 224] //look at the first element
248 is not a multiple of 56. The snake moved Up at the 4th execution, after 3 execution his position will be added to the arrays. But in 3 execution he had moved of 24pixel.
Because of that, I'll get this bug
as you can see, the tail doesn't make a perfect corner.
The snake head doesn't complete his movement of 56 pixels. When I swipe, it leaves his movement and starts another one. Is there a way I can tell the head to always complete his movement before doing another one?
You're sort of trying to use a pixel-based approach to what's basically a grid-based game, but maybe something like this will work...
Make a variable controlling whether movement is allowed:
var moveAllowed = true
Guard your movement code with that variable:
if moveAllowed {
if movesController[0] == true {
playerY += 56
}
...
}
When you schedule the movement action, toggle moveAllowed, and add a completion handler to toggle it back after the action finishes:
...
moveAllowed = false
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt) { self.moveAllowed = true }
(The self.moveAllowed might be just moveAllowed depending on how you have things structured, but I can't tell from your fragments.)
Edit: I just realized that maybe you're setting snakeX and snakeY based on player.position. It's not really clear. In any case, then you'd use the same idea but copying from player.position only when moveAllowed is true. Basically that variable is telling you if the action is still running or not.

How to make a delay in a loop in SpriteKit?

I have made some search here and there but I didn't find (or understand) how to make a delay in a loop in SpriteKit.
This is the idea : I have some SKSpriteNodes sorted in an array and I want to display them on the screen, one every second. The problem is... I simply don't manage to do it (sorry, I'm quite new to this).
let sprite1 = SKSpriteNode(color: .red, size: CGSize(width: 20, height: 20))
sprite1.position = CGPoint(x: 100, y: 100)
let sprite2 = SKSpriteNode(color: .red, size: CGSize(width: 20, height: 20))
sprite2.position = CGPoint(x: 100, y: 300)
let sprite3 = SKSpriteNode(color: .red, size: CGSize(width: 20, height: 20))
sprite3.position = CGPoint(x: 300, y: 100)
let sprite4 = SKSpriteNode(color: .red, size: CGSize(width: 20, height: 20))
sprite4.position = CGPoint(x: 300, y: 300)
let allSprites : [SKSpriteNode] = [sprite1, sprite2, sprite3, sprite4]
let delay = 1.0
var i = 0
while i <= 3
{
let oneSprite = allSprites[i]
self.addChild(oneSprite)
DispatchQueue.main.asyncAfter(deadline : .now() + delay) {
i = i + 1
}
}
If you're asking : this doesn't work at all. It seems that what is inside the DispatchQueue.main.asyncAfter isn't read.
So, if you can help me to understand why, that would be great. I'm not picky, I can't take the answer with a "for" loop.
Regards,
This is a common beginner misunderstanding of event-driven / run-loop-based / GUI programming systems. Some key points to help get on the right track:
SpriteKit is trying to render the scene 60 (or so) times per second.
That means SpriteKit internally has a loop where, once per frame, it calls your code asking what's new (update), then runs its own code to draw the changes (render).
Setup code, or things that happen in response to events (clicks/taps/buttons) are external to this loop, but feed into it: events change state, which the render loop reacts to.
So, if you have a loop in an update method, an event handler, or initial setup code, everything that happens in that loop will happen before SpriteKit gets a chance to draw any of it. That is, if we ignore for a moment the "delay" part of your question, a loop like this...
var i = 0
while i <= 3 {
let oneSprite = allSprites[i]
self.addChild(oneSprite)
}
... will result in none of the sprites being visible right before the loop starts, and all of the sprites being visible after it completes. No amount of introducing delays within the loop will change that, because SpriteKit doesn't get its first chance to draw until after your loop is finished.
Fixing the problem
The best way to do animations and delays in a system like SpriteKit is to make use of the tools it gives you for treating animation declaratively. That is, you tell SpriteKit what you want to have happen over the next several (hundred) frames, and SpriteKit makes that happen — each time it goes through that update/render loop, SpriteKit determines what changes need to be made in the scene to accomplish your animation. For example, if you're running at 60 fps, and you ask for a fade-out animation on some sprite lasting one second, then each frame it'll reduce that sprite's opacity by 1/60.
SpriteKit's tool for declarative animations is SKAction. There are actions for most of the things you'd want to animate, and actions for composing other actions into groups and sequences. In your case, you probably want some combination of the wait and unhide and run(_:onChildWithName) actions:
Give each of your sprites a unique name:
let sprite1 = // ...
sprite1.name = "sprite1"
// etc
Add all the nodes to your scene, but keep the ones you don't want visible yet hidden:
let allSprites : [SKSpriteNode] = [sprite1, sprite2, sprite3, sprite4]
for sprite in allSprites {
sprite.isHidden = true
self.addChild(sprite)
}
Create a sequence action that alternates delays with telling each node to unhide, and run that action on your scene:
let action = SKAction.sequence([
run(.unhide(), onChildNodeWithName: "sprite1"),
wait(forDuration: 1.0),
run(.unhide(), onChildNodeWithName: "sprite2"),
wait(forDuration: 1.0),
// etc
])
self.run(action)
That should accomplish what you're looking for. (Disclaimer: code written in web browser, may require tweaking.) Read on if you want to better understand the situation...
Other alternatives
If you really want to get your head around how the update/render loop works, you could participate directly in that loop. I wouldn't recommend it in general, because it's a lot more code and bookkeeping, but it's useful to know.
Keep a queue (array) of nodes that you want to add or unhide.
In you scene's update method, keep track of how much time has passed.
If it's been at least 1.0 second (or whatever delay you're after) since you last showed a node, add the first one in the array, and remove it from the array.
When the array is empty, you're done.
About DispatchQueue.asyncAfter...
This isn't essential to completing your task, but helpful to understand so you don't get into similar problem later: the word "async" in the call you were using to try delaying things is short for "asynchronous". Asynchronous means that the code you're writing doesn't execute in the order you write it in.
A simplified explanation: Remember that modern CPUs have multiple cores? When CPU 0 is executing your while loop, it gets to the asyncAfter call and passes a note to CPU 1 saying to wait one second and then execute the closure body attached to that asyncAfter call. As soon as it passes that note, CPU 0 continues merrily on its way — it doesn't wait for CPU 1 to receive the node and complete the work that note specifies.
Just from reading the code, I'm not 100% clear on how this approach fails, but fail it certainly does. Either you have an infinite loop, because the while condition never changes, because the work to update i is happening elsewhere and not propagating back to local scope. Or the the changes to i do propagate back, but only after the loop spins an indeterminate number of times waiting for the four asyncAfter calls to finish waiting and execute.

Animation looping although Loop Time is unchecked

#pragma strict
var anim : Animator;
function Start () {
anim = GetComponent("Animator");
}
function Update () {
if (Input.GetButtonDown("Fire1"))
{
anim.SetFloat("hit",1);
}
}
I have set 'hit' float parameter with 'attack' state, if value of 'hit' > 0.1 , then make transition to 'attack' state from 'Idle state'.
Now problem is, when i click mouse left button, the transition goes from Idle to 'attack' and model perform the desired task, then transition goes from 'attack to Idle' and then again transition goes 'Idle to attack' and goes in loop.
I have set LoopTime, Loop poseto unchecked in animationClip, but still the same is happening.
I understand, the above goes in loop, because the 'hit' parameter never becomes < 0.1 so everytime the condition gets true and transition happens to attack state.
If i add the below line to the above Update() function , then transition never happens.
anim.SetFloat("hit",0);
I dont know where is the problem, what should i do ??, i want when i click mouse left button, then model should hit once and then goes back to Idle state, then again if i click mouse left click, then only it should it..
Any help ?? Please
Thanks in advance
you can use trigger for things that happens once and then they should get back to the state they came from like hitting or jumping or etc
anim.SetTrigger("hit");
Single attack animation control parameter should be a Trigger instead of float. This way animator will stay in idle.
If you want to use float than after finishing the animation you have to set the float value to 0. This way parameter doesn't stay greater than 0.1 which is must for float to stay at default animation.

Pygame check for collisions with the top most foreground rect

I have 10 sprites which I objects of the main sprite I wrote with different images and starting positions etc. But they all behave the same way. They are sub sprites of the main sprite.
I want to be able to hold mouse click on one's rect and move it round the screen which works perfectly fine. But the problem is they all have the same controls click and drag to move them. So if I am clicking on one of the sprite rects and I drag it over another one it picks it up as well. And I don't want that to happen.
Is there a way to only check for collisions with the top most foreground rect or if someone could explain a way of doing this that would achieve similar results. I have had a look at the rect documentation but I can't find a solution.
def update(self,):
self.move(self.rect)
def move(self,rect):
if pygame.mouse.get_pressed() == (1, 0, 0) and the_rect.collidepoint(pygame.mouse.get_pos()):
self.state = 1
elif pygame.mouse.get_pressed() == (0, 0, 0) and the_rect.collidepoint(pygame.mouse.get_pos()):
self.state = 0
if self.state == 0:
the_rect.centerx = the_rect.centerx
the_rect.centery = the_rect.centery
elif self.state == 1:
(the_rect.centerx, the_rect.centery) = pygame.mouse.get_pos()
Rather than using the pygame.mouse.get_pressed() function, use the event queue and check for a pygame.MOUSEBUTTONDOWN event. It will only get fired once when the button is first pressed.