Rotate sprite by touch with a limited rotation speed
I have a turret which ... should target the finger position, but not
immediately, by taking time to turn.
You won't be able to get away with SKActions for something like this. You can try but it will be really messy and inefficient. You need real-time motion control for something like this because the angular velocity of your turret needs to change constantly depending on the touch position.
So I wrote you a quick example project showing how to calculate the angular velocity. The project handles all special cases as well, such as preventing the angle from jumping over your target rotation.
import SpriteKit
class GameScene: SKScene {
let turret = SKSpriteNode(imageNamed: "Spaceship")
let rotationSpeed: CGFloat = CGFloat(M_PI) //Speed turret rotates.
let rotationOffset: CGFloat = -CGFloat(M_PI/2.0) //Controls which side of the sprite faces the touch point. I offset the angle by -90 degrees so that the top of my image faces the touch point.
private var touchPosition: CGFloat = 0
private var targetZRotation: CGFloat = 0
override func didMoveToView(view: SKView) {
turret.physicsBody = SKPhysicsBody(rectangleOfSize: turret.size)
turret.physicsBody!.affectedByGravity = false
turret.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(turret)
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
calculateAngleToTouch(touches.anyObject() as UITouch)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
calculateAngleToTouch(touches.anyObject() as UITouch)
}
func calculateAngleToTouch(touch: UITouch) {
let position = touch.locationInNode(self)
let angle = atan2(position.y-turret.position.y, position.x-turret.position.x)
targetZRotation = angle + rotationOffset
}
override func update(currentTime: NSTimeInterval) {
var angularDisplacement = targetZRotation - turret.zRotation
if angularDisplacement > CGFloat(M_PI) {
angularDisplacement = (angularDisplacement - CGFloat(M_PI)*2)
} else if angularDisplacement < -CGFloat(M_PI) {
angularDisplacement = (angularDisplacement + CGFloat(M_PI)*2)
}
if abs(angularDisplacement) > rotationSpeed*(1.0/60.0) {
let angularVelocity = angularDisplacement < 0 ? -rotationSpeed : rotationSpeed
turret.physicsBody!.angularVelocity = angularVelocity
} else {
turret.physicsBody!.angularVelocity = 0
turret.zRotation = targetZRotation
}
}
}
Rotate an object in its direction of motion
It's fairly straightforward to rotate a sprite in the direction of its motion. You can do this by converting the x
and y
components of the sprite's velocity into an angle with the atan2
function. You should rotate the sprite only when its speed is greater than some nominal value to prevent the sprite from resetting to zero degrees when the sprite's speed is (nearly) zero.
If we extend CGVector
to compute speed and angle from the velocity's components by
extension CGVector {
func speed() -> CGFloat {
return sqrt(dx*dx+dy*dy)
}
func angle() -> CGFloat {
return atan2(dy, dx)
}
}
we can rotate a sprite to face in the direction of its motion with
override func didSimulatePhysics() {
if let body = sprite.physicsBody {
if (body.velocity.speed() > 0.01) {
sprite.zRotation = body.velocity.angle() - offset
}
}
}
where offset = CGFloat(M_PI_2)
if your sprite faces up when zRotation
is zero, and offset = 0
if your sprite faces to the right when zRotation
is zero.
Rotating sprite with finger in Cocos2d
@interface MainScene : CCLayer {
CCSprite *dial;
CGFloat dialRotation;
}
+ (id)scene;
@end
---------------------------------
@implementation MainScene
+ (id)scene
{
CCScene *scene = [CCScene node];
CCLayer *layer = [MainScene node];
[scene addChild:layer];
return scene;
}
- (id)init
{
if((self = [super init])) {
CCLOG(@"MainScene init");
CGSize size = [[CCDirector sharedDirector]winSize];
dial = [CCSprite spriteWithFile:@"dial.png"];
dial.position = ccp(size.width/2,dial.contentSize.height/2);
[self addChild:dial];
self.isTouchEnabled = YES;
[self scheduleUpdate];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)update:(ccTime)delta
{
dial.rotation = dialRotation;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
//acquire the previous touch location
CGPoint firstLocation = [touch previousLocationInView:[touch view]];
CGPoint location = [touch locationInView:[touch view]];
//preform all the same basic rig on both the current touch and previous touch
CGPoint touchingPoint = [[CCDirector sharedDirector] convertToGL:location];
CGPoint firstTouchingPoint = [[CCDirector sharedDirector] convertToGL:firstLocation];
CGPoint firstVector = ccpSub(firstTouchingPoint, dial.position);
CGFloat firstRotateAngle = -ccpToAngle(firstVector);
CGFloat previousTouch = CC_RADIANS_TO_DEGREES(firstRotateAngle);
CGPoint vector = ccpSub(touchingPoint, dial.position);
CGFloat rotateAngle = -ccpToAngle(vector);
CGFloat currentTouch = CC_RADIANS_TO_DEGREES(rotateAngle);
//keep adding the difference of the two angles to the dial rotation
dialRotation += currentTouch - previousTouch;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
EDIT: I am sorry I can't get this to stay in code mode. But this is the solution and it should be easy to implement in your code. cheers!
Rotation of sprite by using sprites velocity (swift) flappy bird game
bird.physicsBody?.allowsRotation = true
var velocityvector = bird.physicsBody?.velocity
let angle = atan2(velocityvector?.dy ?? 0, velocityvector?.dx ?? 0)
let rotateAction = SKAction.rotate(byAngle: angle, duration: 0.5)
// Or let rotateAction = SKAction.rotate(toAngle: angle, duration: 0.5)
bird.run(rotateAction)
Related Topics
How to Hide the Home Indicator with Swiftui
How to Prevent a Remote Notification from Being Displayed
Flutter: Disable Swipe to Navigate Back in iOS and Android
Optional Binding Succeeds If It Shouldn'T
Perform Segue from App Delegate Swift
Perform Action by Clicking on Some Word in Uitextview or Uilabel
How to Properly Remove Node When Out of Screen Bounds
Setting Layoutmargins of Uiview Doesn't Work
Delphi Xe4 iOS Can't Connect to Paserver
Hide Red Recording Status Bar in iOS App When Not Recording
Ms Excel Type Spreadsheet Creation Using Objective-C for iOS App
Creating an Image Format with an Unknown Type Is an Error Objective-C Xcode 8
How to Draw on an Image in Swift
iOS Color to Transparent in Uiimage
How to Delete an App Which Is Waiting for Upload
Get Sms Broadcast with Text Body Without Jailbreak But Private Frameworks in iOS