我有一个游戏,我想向左或向右移动一个SKNode,具体取决于用户是否触摸屏幕的左侧或右侧。如何在用户触摸屏幕时将恒定移动应用于节点?
答案 0 :(得分:35)
你真的有三种选择来保持速度。
一种是每次更新时只需将速度重新设置为所需的速度。在这种情况下,我选择了200,200。这将使您的动作完全静止。
-(void)update:(NSTimeInterval)currentTime {
node.physicsBody.velocity=CGVectorMake(200, 200);
}
第二种选择是保持速度,但使用速率因子为运动失真留出空间。下面的示例将速度保持在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);
}
您拥有的第三个选项是设置速度或应用脉冲 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元素等简单的事情,您不需要在每一步跟踪它们的运动,因此使用动画
答案 1 :(得分:5)
添加一个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:对于连续操作,特别是移动,行为是非常糟糕的,因为它们是基于时间的,即应该在一定时间后结束。但是,如果行动的时间在到达目的地之前耗尽怎么办?
答案 2 :(得分:1)
我正在直接设置velocity.dx,但我不推荐这种方法,因为它完全忽略了之前的速度。
public func setSpeed(speed: CGFloat) {
physicsBody.velocity.dx = speed
}
我想为您提供一个解决方案,该解决方案可以更好地使用现有的spritekit物理引擎,但我没有,我会为它提供赏金,因为我也需要一个解决方案。
对于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
}
答案 3 :(得分:1)
以下是您在数学上的确切方式...
所以,在这个例子中,手指想要移动物品。
它确切地定义了你希望它在帧时间内移动多少。
(在许多(当然不是全部)情况下,你将质量作为一个标准化的“一公斤”。(如果你的物理学正在研究“抽象”的身体 - 你正在推动“图标”, “blob”之类的东西 - 这样做,使用1.0公斤。当然,如果你的物理学是坦克,足球或其他任何东西,你必须使用现实世界的价值观。)有了“一公斤”的质量,你就是这样做的。 )
享受!
答案 4 :(得分:0)
取决于您是否想要使用物理力量或动作。
像这样的东西
-(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 中施加一小部分力量,精灵应该停止。
答案 5 :(得分:0)
使用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);
}
答案 6 :(得分:0)
如果您的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的答案。
答案 7 :(得分:0)
这非常简单,例如每秒移动100px:
func touchUp(atPoint pos : CGPoint)
{
node.removeAllActions()
let pxPerSecond : CGFloat = 100.0;
var length = (pos - node.position).length()
var totalSeconds = length / pxPerSecond;
let moveAction = SKAction.move(to: pos, duration: Double(totalSeconds))
node.run(moveAction)
}
您可以在本文https://www.raywenderlich.com/71-spritekit-tutorial-for-beginners
中获得减法和规范化操作的替代答案 8 :(得分:-1)
如果你必须从左到右停止在固定位置的动作。
这适用于Swift和objective-c。
你必须在physicsBody中使用:
node.position = CGPoint(X:.....,Y:.....) node.physicsBody = nil
答案 9 :(得分:-3)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
if (touchLocation.x > 50) {
// Do whatever..
_timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(movePlayer:) userInfo:nil repeats:YES];
NSLog(@"Right");
}
else if (touchLocation.x < 50){
////// use another NSTimer to call other function////
NSLog(@"left");
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if (_timer != nil) {
[_timer invalidate];
_timer = nil;
}
}
-(void)movePlayer:(id)sender
{
hero.physicsBody.velocity = CGVectorMake(400, 0);
}