ユーザーが画面の左側と右側のどちらに触れたかに応じて、SKNodeを左または右に移動したいゲームがあります。ユーザーが画面に触れている間、ノードに一定の動きを適用するにはどうすればよいですか?
速度を維持するための3つのオプションがあります。
1つは、更新のたびにベロシティを希望の量にリセットし続けることです。この場合、私は200,200を選択しました。これにより、モーションが完全に静的になります。
-(void)update:(NSTimeInterval)currentTime {
node.physicsBody.velocity=CGVectorMake(200, 200);
}
2番目のオプションは、速度を維持しながら、レート係数を使用してモーションの歪みの余地を残すことです。以下の例では、速度を200,200に維持しますが、瞬時には維持しません。代わりに、それはレートに依存します。これにより、モーションがはるかにリアルになり、静的さが少し少なくなります。たとえば、キャラクターが一定の速度で動いていて方向を変えたとすると、新しい一定の速度の値に変わるときに小さな加速度が表示され、動きの変化が静的でなく動的になります。このオプションを強くお勧めします。
-(void)update:(NSTimeInterval)currentTime {
CGFloat rate = .05;
CGVector relativeVelocity = CGVectorMake(200-node.physicsBody.velocity.dx, 200-node.physicsBody.velocity.dy);
node.physicsBody.velocity=CGVectorMake(node.physicsBody.velocity.dx+relativeVelocity.dx*rate, node.physicsBody.velocity.dy+relativeVelocity.dy*rate);
}
3番目のオプションは、速度を設定するか、インパルスを適用することです[〜#〜] once [〜#〜](上記のようなすべてのフレームとは対照的に)が、重力をオフにしますと空気抵抗。このアプローチの問題は、常に速度が維持されるとは限らないことです。外力(摩擦、他の物体との衝突など)が作用するまで、一定の速度を維持するだけです。このアプローチは、オブジェクトを一定の速度で移動させながら、完全に動的なままにしておきたい場合に機能します。オブジェクトの速度を制御および/または維持したい場合は、このオプションをまったくお勧めしません。これについて私が考えることができる唯一のシナリオは、小惑星が画面の周りを一定の速度で浮かんでいるが、それでも外力の影響を受けている(つまり、2つの小惑星が衝突して新しい一定の速度になる)ゲームです。
node.physicsBody.velocity=CGVectorMake(200, 200);
node.physicsBody.linearDamping = 0; //You may want to remove the air-resistance external force.
node.physicsBody.affectedByGravity = false; //You may want to remove the gravity external force
これは私がSwiftオプション2を使用して書いたプロジェクトの例です。あなたの説明に一致させようとしました。ノードを一定の速度で左右に動かすだけです。必ずレート係数を試してください。
import SpriteKit
class GameScene: SKScene {
var Sprite: SKSpriteNode!
var xVelocity: CGFloat = 0
override func didMoveToView(view: SKView) {
Sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
Sprite.physicsBody = SKPhysicsBody(rectangleOfSize: Sprite.size)
Sprite.physicsBody.affectedByGravity = false
Sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
self.addChild(Sprite)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let location = touch.locationInNode(self)
if (location.x<self.size.width/2.0) {xVelocity = -200}
else {xVelocity = 200}
}
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
xVelocity = 0
}
override func update(currentTime: CFTimeInterval) {
let rate: CGFloat = 0.5; //Controls rate of motion. 1.0 instantaneous, 0.0 none.
let relativeVelocity: CGVector = CGVector(dx:xVelocity-Sprite.physicsBody.velocity.dx, dy:0);
Sprite.physicsBody.velocity=CGVector(dx:Sprite.physicsBody.velocity.dx+relativeVelocity.dx*rate, dy:0);
}
}
注:リアルタイムモーションの場合はSKActionsに近づかないようにしてください。アニメーションにのみ使用してください。
リアルタイムモーションとは、シミュレーションの各ステップでオブジェクトの位置と速度が重要になるモーションを意味します。一方、アニメーションでは、実際には、ある期間の開始点と終了点のみに関心があります。たとえば、キャラクターが動くゲームでは、リアルタイムの動きが必要です。シミュレーションの各ステップでキャラクターの位置と速度を監視し、特定の間隔で調整できる可能性がある必要があります。ただし、UI要素の移動などの単純なものについては、各ステップでそれらの動きを追跡する必要がないため、アニメーションを使用してください
Ivarを追加します。
CGPoint velocity;
モーションを設定します。たとえば、右に移動します。
velocity.x = 5;
ステップごとに速度を積分します。
-(void) update:(NSTimeInterval)currentTime
{
CGPoint pos = self.position;
pos.x += velocity.x;
pos.y += velocity.y;
self.position = pos;
}
PS:アクションは時間ベースである、つまり特定の期間の後に終了することになっているため、継続的な操作、特に移動には悪いことで有名です。しかし、アクションが目的地に到達する前にアクションの時間がなくなった場合はどうなりますか?
私はvelocity.dxを直接設定していますが、前の速度を完全に無視するため、このアプローチはお勧めしません。
public func setSpeed(speed: CGFloat) {
physicsBody.velocity.dx = speed
}
既存のスプライトキット物理エンジンでより適切に機能するソリューションを提供したいのですが、私にはありません。これに対するソリューションも必要なので、賞金を提供します。
Swiftのタッチの場合、次のことができます。
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
player.setSpeed(100) // You can use touches.anyObject().position to see the touch position and implement left/right movement
}
SKNode
にphysicsBody
がある場合は、それに力を加えます。
@interface GameScene()
@property (nonatomic) CGVector force;
@end
@implementation GameScene
- (void)update:(CFTimeInterval)currentTime {
[self.player.physicsBody applyForce:self.force];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.sideForce = CGVectorMake(3000, 0);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.sideForce = CGVectorMake(0, 0);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
self.sideForce = CGVectorMake(0, 0);
}
@end
そうでない場合は、LearnCocos2Dの回答を使用してください。
物理的な力またはアクションを使用するかどうかによって異なります。
このようなもの
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
if (loc.x > self.frame.size.width * 0.5) {
NSLog(@"move right");
// move the Sprite using an action
// or set physics body and apply a force
}
else {
NSLog(@"move left");
}
}
}
アクションを使用することにした場合は、touchesEndedで、移動するスプライトからすべてのアクションを削除します。
touchesBeganで小さな力を加えている場合、スプライトは停止するはずです。
PhysicsBodyのvelocityプロパティを使用します。変数rightは、タッチポイントからオブジェクト(この場合はself)を引いたものであり、関連する速度を提供します。
-(void)segmentedTouchBegan:(NSValue *)p{
CGPoint point = [p CGPointValue];
int right = (point.x - self.screenWidth/2.0f)/ABS((point.x - self.screenWidth/2.0f)); //1 if right -1 if left
self.physicsBody.velocity = CGVectorMake(SHIFT*right, 0);
}
-(void)segmentedTouchMoved:(NSValue *)p{
CGPoint point = [p CGPointValue];
int right = (point.x - self.screenWidth/2.0f)/ABS((point.x - self.screenWidth/2.0f)); //1 if right -1 if left
self.physicsBody.velocity = CGVectorMake(SHIFT*right, 0);
}
-(void)segmentedTouchEnded:(NSValue *)p{
self.physicsBody.velocity = CGVectorMake(0, 0);
}