- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我在建模以正确方式移动播放器节点时遇到了一些麻烦我想要的。
这是我第一次涉足 Spritekit,我已经具备并运行了基础知识(我有一个静态背景,添加了播放器节点,并且有一个带边界检查的可播放边界矩形)
我添加玩家移动的方式是跟踪开始触摸位置和将其存储在场景类范围变量(称为 beginningTouchPosition)中,并存储当前触摸位置(称为 currentTouchPosition)。我还跟踪玩家 Sprite 节点位置 (currentPlayerPosition)
我所做的是在 onTouchesBegan 上更新“beginningTouchPosition”,然后在 onTouchesMoved 中更新“currentTouchPosition”,这样我就可以通过获取相对于“beginningTouchPosition”的方向来了解用户希望他的船移动的方向,因为他/她移动他们的手指。 “currentTouchPosition”与“beginningTouchPosition”的距离也决定了飞船移动的速度。
我通过使用上述几点创建 CGVector 并将其与 SKAction.MoveBy 调用一起使用来移动更新中的播放器。
我这样做是因为我希望用户能够触摸屏幕上的任何位置以控制移动。
我希望玩家如何移动。我宁愿通过在特定方向上应用特定的设定速度和设定的加速度来让船移动。因此,当手指移动时,玩家将在 1/2 秒的时间内从零加速到 1,并继续朝该方向前进,直到手指再次移动或抬起。
如果手指被抬起,那么船应该继续沿最后一个方向移动,但开始减速,直到速度回到零。
我基本上是在尝试模拟一个物体在零重力下如何运动,具有明显的非现实减速特征。
我找到了展示如何将对象移向手指触摸的教程,但这不是我想要的,因为我正在尝试制作一款横向卷轴太空射击游戏,玩家可以在可玩区域内的任何地方移动,而不是简单地上下移动。类似于旧的复古游戏“复仇女神”,见下图:
我附上了我的播放器类代码和场景代码,以便更好地可视化我目前是如何做的。
任何有关如何在指定方向上应用速度和加速度的文献的指针都会有所帮助:)
import SpriteKit
// Global
/*
Level_1 set up and control
*/
class Level_1: SKScene {
// Instance variables
var lastUpdateTime:NSTimeInterval = 0
var dt:NSTimeInterval = 0
var player = Player() // Sub classed SKSpriteNode for all player related stuff
var currentTouchPosition: CGPoint!
var beginningTouchPosition:CGPoint!
var currentPlayerPosition: CGPoint!
let playableRectArea:CGRect
override init(size: CGSize) {
// Constant - Max aspect ratio supported
let maxAspectRatio:CGFloat = 16.0/9.0
// Calculate playable height
let playableHeight = size.width / maxAspectRatio
// Determine margin on top and bottom by subtracting playable height
// from scene height and then divide by 2
let playableMargin = (size.height-playableHeight)/2.0
// Calculate the actual playable area rectangle
playableRectArea = CGRect(x: 0, y: playableMargin,
width: size.width,
height: playableHeight)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
currentTouchPosition = CGPointZero
beginningTouchPosition = CGPointZero
let background = SKSpriteNode(imageNamed: "background1")
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.zPosition = -1
self.addChild(background)
currentPlayerPosition = CGPoint(x: 100, y: size.height/2)
player.position = currentPlayerPosition
self.addChild(player)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
currentTouchPosition = touch.locationInNode(self)
}
let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x)
let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y)
player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch: AnyObject in touches {
beginningTouchPosition = touch.locationInNode(self)
currentTouchPosition = beginningTouchPosition
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
currentPlayerPosition = player.position
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
}else{
dt = 0
}
lastUpdateTime = currentTime
player.boundsCheckPlayer(playableRectArea)
}
}
import Foundation
import SpriteKit
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player : UInt32 = 0b1 // 1
static let Enemy : UInt32 = 0b10 // 2
}
class Player: SKSpriteNode{
init(){
// Initialize the player object
let texture = SKTexture(imageNamed: "ship1")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.xScale = 2
self.yScale = 2
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.zPosition = 1
// Player physics
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = false
self.physicsBody?.categoryBitMask = PhysicsCategory.Player
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Check if the player sprite is within the playable area bounds
func boundsCheckPlayer(playableArea: CGRect){
let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea))
let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea))
if(self.position.x <= bottomLeft.x){
self.position.x = bottomLeft.x
// velocity.x = -velocity.x
}
if(self.position.x >= topRight.x){
self.position.x = topRight.x
// velocity.x = -velocity.x
}
if(self.position.y <= bottomLeft.y){
self.position.y = bottomLeft.y
// velocity.y = -velocity.y
}
if(self.position.y >= topRight.y){
self.position.y = topRight.y
// velocity.y = -velocity.y
}
}
/*
Move the player in a certain direction by a specific amount
*/
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.moveBy(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
}
最佳答案
基本上,我们需要一个零重力场景和一个玩家,其中的触摸会导致力类型的物理 Action 。这不是 moveBy 类型的数字 Action ,它通过这样那样简单地在屏幕上移动角色。
我继续测试代码,尝试为您提供与您描述的内容类似的内容。我稍微修改了你的一些代码......让它与我自己的设置一起工作,因为你没有提供你的 GameViewController 代码所以问你是否有任何问题。
我在代码末尾提供了注释,上面写着//IMPORTANT CODE,旁边有一个#。
这里详细说明了为什么要使用每个“重要代码”
我们需要物理学来完成您所描述的,因此首先要确保玩家类有一个物理体。 body 将是动态的并受重力影响(零重力),但是为了游戏玩法,您可能想要稍微调整一下重力。
let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size() )
self.physicsBody = body
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = true
self.physicsBody?.affectedByGravity = true
由于您想要零重力,我们需要在我们的场景中改变物理世界的重力
scene?.physicsWorld.gravity = CGVectorMake(0, 0)
接下来我们更改您的 movePlayerBy() 以使用力而不是简单的数字移动。我们使用 SKAction.applyForce 来做到这一点。
这为您提供了一个基于与滑动相关的力的设置。但是,无论滑动多么用力,您可能都需要恒定的速度。您可以通过对向量进行归一化来做到这一点。请参阅此处了解以某种方式提出该问题的人以及它如何在此处应用(http://www.scriptscoop2.com/t/adc37b4f2ea8/swift-giving-a-physicsbody-a-constant-force.html)
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
print("move player")
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
在下面的暂停 Action 之前,您可以尝试其他方法来减速,例如最初使用的负力。
还有许多其他方法可以使它更像真正的减速……比如第二个序列,在我们将速度硬编码为 0 之前,在设定的时间间隔从速度中减去 -1 直到它达到 0。但是,从游戏玩法的角度来看,这取决于您。
所以这应该足以给你一个想法。
func stopMoving() {
let delayTime: NSTimeInterval = 0.5 // 0.5 second pause
let stopAction: SKAction = SKAction.runBlock{
self.physicsBody?.velocity = CGVectorMake(0, 0)
}
let pause: SKAction = SKAction.waitForDuration(delayTime)
let stopSequence: SKAction = SKAction.sequence([pause,stopAction])
self.runAction(stopSequence)
}
我们更改 touchesEnded() 以调用 stopMoving() .. 但是,在没有这个的情况下尝试它可以看到它没有那种“减速”。
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
player.stopMoving()
}
其他说明。
目前,使用我创建的代码,边界只能捕获左右两侧的玩家……我不确定这是否会发生在您的设置中。但是,由于这是另一个需要弄清楚的问题,我没有进一步研究它。
这是我使用的代码...我提供它是因为我为了测试做了一些其他的小改动。除了放置新的重要代码段之外,我不会担心任何其他事情。
GameScene.Swift
import SpriteKit
// Global
/*
Level_1 set up and control
*/
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
class Level_1: GameScene {
// Instance variables
var lastUpdateTime:NSTimeInterval = 0
var dt:NSTimeInterval = 0
var player = Player() // Sub classed SKSpriteNode for all player related stuff
var currentTouchPosition: CGPoint = CGPointZero
var beginningTouchPosition:CGPoint = CGPointZero
var currentPlayerPosition: CGPoint = CGPointZero
var playableRectArea:CGRect = CGRectZero
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// IMPORTANT CODE 2 //
scene?.physicsWorld.gravity = CGVectorMake(0, 0)
// Constant - Max aspect ratio supported
let maxAspectRatio:CGFloat = 16.0/9.0
// Calculate playable height
let playableHeight = size.width / maxAspectRatio
// Determine margin on top and bottom by subtracting playable height
// from scene height and then divide by 2
let playableMargin = (size.height-playableHeight)/2.0
// Calculate the actual playable area rectangle
playableRectArea = CGRect(x: 0, y: playableMargin,
width: size.width,
height: playableHeight)
currentTouchPosition = CGPointZero
beginningTouchPosition = CGPointZero
let background = SKSpriteNode(imageNamed: "Level1_Background")
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.zPosition = -1
self.addChild(background)
// CHANGED TO Put my own texture visible on the screen
currentPlayerPosition = CGPoint(x: size.width/2, y: size.height/2)
player.position = currentPlayerPosition
self.addChild(player)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
currentTouchPosition = touch.locationInNode(self)
}
let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x)
let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y)
player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
player.removeAllActions()
// IMPORTANT CODE 5 //
player.stopMoving()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
print("touch")
for touch: AnyObject in touches {
beginningTouchPosition = touch.locationInNode(self)
currentTouchPosition = beginningTouchPosition
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
currentPlayerPosition = player.position
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
}else{
dt = 0
}
lastUpdateTime = currentTime
player.boundsCheckPlayer(playableRectArea)
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
Player.swift
import Foundation
import SpriteKit
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player : UInt32 = 0b1 // 1
static let Enemy : UInt32 = 0b10 // 2
}
class Player: SKSpriteNode{
init(){
// Initialize the player object
let texture = SKTexture(imageNamed: "Player1")
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
self.xScale = 2
self.yScale = 2
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.zPosition = 1
// Player physics
// IMPORTANT CODE 1 //
let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size() )
self.physicsBody = body
self.physicsBody?.allowsRotation = false
self.physicsBody?.dynamic = true
self.physicsBody?.affectedByGravity = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Player
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// Check if the player sprite is within the playable area bounds
func boundsCheckPlayer(playableArea: CGRect){
let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea))
let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea))
if(self.position.x <= bottomLeft.x){
self.position.x = bottomLeft.x
// velocity.x = -velocity.x
}
if(self.position.x >= topRight.x){
self.position.x = topRight.x
// velocity.x = -velocity.x
}
if(self.position.y <= bottomLeft.y){
self.position.y = bottomLeft.y
// velocity.y = -velocity.y
}
if(self.position.y >= topRight.y){
self.position.y = topRight.y
// velocity.y = -velocity.y
}
}
/*
Move the player in a certain direction by a specific amount
*/
// IMPORTANT CODE 3 //
func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){
print("move player")
let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue)
let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration)
self.runAction(movePlayerAction)
}
// IMPORTANT CODE 4 //
func stopMoving() {
let delayTime: NSTimeInterval = 0.5 // 0.5 second pause
let stopAction: SKAction = SKAction.runBlock{
self.physicsBody?.velocity = CGVectorMake(0, 0)
}
let pause: SKAction = SKAction.waitForDuration(delayTime)
let stopSequence: SKAction = SKAction.sequence([pause,stopAction])
self.runAction(stopSequence)
}
}
关于ios - 模拟零重力风格的球员运动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37807227/
我喜欢 MVP 概念,它对我组织代码有很大帮助,但每次我需要从外部应用程序、对话框或请求权限获取结果时,我都会遇到同样的问题问题 - 架构的哪个组件应该关心这个? 例如,我们需要实现以下行为: 点击按
我正在尝试使用 MVP 模式重构我的应用程序,并且希望将来用 View 替换 fragment 。 当 Activity 配置发生变化时,如何存储演示者的缓存数据?我不想使用 SQLite、共享首选项
这个问题更倾向于范式。为什么我们不在 MVP 环境中使用事件总线而不是监听器?通常,“P”部分具有 View 和模型引用的依赖注入(inject)。当然,这有一个优势,即通过 Presenter 显示
在我的基于 MVP 模式(被动 View )的应用程序 (.NET) 中,我将消息框分离到 View 中,这样当我测试我的演示者和模型时,我的测试代码不会受到需要向用户展示一些信息的影响信息。但是当需
我是一名优秀的程序员,十分优秀!