gpt4 book ai didi

ios - 以一定速度在屏幕上随机移动物体

转载 作者:可可西里 更新时间:2023-11-01 00:57:08 24 4
gpt4 key购买 nike

我需要让我的对象在屏幕上随机移动。当它们移动时,该对象正在寻找一定半径范围内的另一个对象。

我找到了下面的链接,并实现了 getDuration 函数。但是我遇到了与主题所有者相同的故障。我可以看到应该可以通过删除运行操作来修复。

Moving an object across the screen at a certain speed.(Sprite Kit)

视频:https://www.youtube.com/watch?v=jHE5RC-mvwU

但是我现在已经试了好几种方案了,还是不行。当我停止 Action 时,我的对象就会停止移动。

有人可以告诉我,在哪里终止我的 Action ,在 moveToWaypoint 中创建?我需要我的对象移动到一个随机的路径点,但如果一个特定的对象进入半径内,那么它应该为壁橱对象设置一个新的路径点并再次开始操作。

代码:

///Called in update method
func move(scene: GameplayScene) {
checkForWaypoint()
checkForFood(scene: scene)
moveToWaypoint()
}

///Creates a new waypoint
func createWaypoint() {
waypoint = CGPoint(x: randomBetweenNumbers(firstNum: minX, secondNum: maxX), y: randomBetweenNumbers(firstNum: minX, secondNum: maxX))
}

override func checkForFood(scene: GameplayScene) {
var closesObject: SKNode? = nil

scene.enumerateChildNodes(withName: "Food") {
node, _ in
let distance = node.position.distanceFromCGPoint(point: self.position)

if distance < self.FOOD_RANGE {
closesObject = node
}
}

if hungryState == HungryState.hungry {
if closesObject != nil {
waypoint = closesObject?.position
moveState = MoveState.movingToFood
} else {
if moveState == MoveState.movingToFood {
createWaypoint()
}
}
}
}

///Moves the object to the waypoint
func moveToWaypoint () {
let action = SKAction.move(to: waypoint!, duration: getDuration(pointA: position, pointB: waypoint!, speed: 25))
self.run(action)
}

///Calcuate a speed between to cordinates
private func getDuration(pointA:CGPoint,pointB:CGPoint,speed:CGFloat)-> TimeInterval {
let xDist = (pointB.x - pointA.x)
let yDist = (pointB.y - pointA.y)
let distance = sqrt((xDist * xDist) + (yDist * yDist));
let duration : TimeInterval = TimeInterval(distance/speed)
return duration
}

编辑:

Gamescene 类的更新函数

override func update(_ currentTime: TimeInterval) {
moveFish()
}

private func moveFish() {
for node in self.children {
if node.name != nil {
switch node.name {
case "Fish"?:
let fishToMove = node as! Fish

fishToMove.move(scene: self)

default:
break
}
}
}
}

最佳答案

您在这里尝试解决的问题可以通过两种方式完成。首先是使用物理学,其次当然是没有它。我决定不学习物理,因为这显然是您的做法。

简而言之,这些示例中的鱼在周围没有食物时使用 SKAction 移动。当食物在范围内时,鱼会使用 update: 方法移动到它们的目标。当鱼吃掉它的发现物时,它会继续使用 SKAction 移动。

此外,在这之前,这里有一些我从 Stackoverflow 借来的有用的扩展,您将来可能会发现它们很有用:

import SpriteKit
import GameplayKit

//Extension borrowed from here : https://stackoverflow.com/a/40810305
extension ClosedRange where Bound : FloatingPoint {
public func random() -> Bound {
let range = self.upperBound - self.lowerBound
let randomValue = (Bound(arc4random_uniform(UINT32_MAX)) / Bound(UINT32_MAX)) * range + self.lowerBound
return randomValue
}
}
//Extension borrowed from here : https://stackoverflow.com/a/37760551
extension CGRect {
func randomPoint() -> CGPoint {
let origin = self.origin
return CGPoint(x:CGFloat(arc4random_uniform(UInt32(self.width))) + origin.x,
y:CGFloat(arc4random_uniform(UInt32(self.height))) + origin.y)
}
}

//Extension borrowed from here: https://stackoverflow.com/a/33292919

extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
return abs(CGFloat(hypotf(Float(point.x - x), Float(point.y - y))))
}
}

现在有一个像您这样的 Fish 类,它只有很少的方法和一个物理体,仅用于检测食物和鱼之间的接触,但那都是来自物理学的。这是 Collider 结构,以防您想知道我是如何定义它的:

struct Collider{
static let food : UInt32 = 0x1 << 0
static let fish : UInt32 = 0x1 << 1
static let wall : UInt32 = 0x1 << 2
}

现在回到 Fish 类...我已经在代码中添加了注释,所以我想不需要解释这些方法的作用。这是代码:

class Fish:SKSpriteNode{
private let kMovingAroundKey = "movingAround"
private let kFishSpeed:CGFloat = 4.5
private var swimmingSpeed:CGFloat = 100.0
private let sensorRadius:CGFloat = 100.0
private weak var food:SKSpriteNode! = nil //the food node that this fish currently chase

override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)

physicsBody = SKPhysicsBody(rectangleOf: size)
physicsBody?.affectedByGravity = false
physicsBody?.categoryBitMask = Collider.fish
physicsBody?.contactTestBitMask = Collider.food
physicsBody?.collisionBitMask = 0x0 //No collisions with fish, only contact detection
name = "fish"

let sensor = SKShapeNode(circleOfRadius: 100)
sensor.fillColor = .red
sensor.zPosition = -1
sensor.alpha = 0.1
addChild(sensor)
}

func getDistanceFromFood()->CGFloat? {


if let food = self.food {

return self.position.distance(point: food.position)
}
return nil

}

func lock(food:SKSpriteNode){

//We are chasing a food node at the moment
if let currentDistanceFromFood = self.getDistanceFromFood() {

if (currentDistanceFromFood > self.position.distance(point: food.position)){
//chase the closer food node
self.food = food
self.stopMovingAround()
}//else, continue chasing the last locked food node

//We are not chasing the food node at the moment
}else{
//go and chase then
if food.position.distance(point: self.position) <= self.sensorRadius {

self.food = food
self.stopMovingAround()
}
}
}

//Helper method. Not used currently. You can use this method to prevent chasing another (say closer) food while already chasing one
func isChasing(food:SKSpriteNode)->Bool{

if self.food != nil {

if self.food == food {
return true
}
}

return false
}

func stopMovingAround(){

if self.action(forKey: kMovingAroundKey) != nil{
removeAction(forKey: kMovingAroundKey)
}
}


//MARK: Chasing the food
//This method is called many times in a second
func chase(within rect:CGRect){

guard let food = self.food else {

if action(forKey: kMovingAroundKey) == nil {
self.moveAround(within: rect)
}
return
}

//Check if food is in the water
if rect.contains(food.frame.origin) {

//Take a detailed look in my Stackoverflow answer of how chasing works : https://stackoverflow.com/a/36235426

let dx = food.position.x - self.position.x
let dy = food.position.y - self.position.y

let angle = atan2(dy, dx)

let vx = cos(angle) * kFishSpeed
let vy = sin(angle) * kFishSpeed

position.x += vx
position.y += vy

}
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func moveAround(within rect:CGRect){

if scene != nil {

//Go randomly around the screen within view bounds
let point = rect.randomPoint()

//Formula: time = distance / speed
let duration = TimeInterval(point.distance(point: position) / self.swimmingSpeed)
let move = SKAction.move(to: point, duration: duration)
let block = SKAction.run {
[unowned self] in

self.moveAround(within: rect)
}
let loop = SKAction.sequence([move,block])

run(loop, withKey: kMovingAroundKey)
}
}
}

所以基本上,有一些方法可以在鱼不追逐食物的时候四处移动。还有一种方法可以停止这个(无限) Action (SKAction)。最重要的方法是 chase(within rect:) 方法。该方法在场景的 update() 方法中调用,并定义鱼将如何以及何时(尝试)追逐食物。

现在是 GameScene:

//MARK: GameScene
class GameScene: SKScene, SKPhysicsContactDelegate {

private var nodesForRemoval:[SKNode] = []
private var water = SKSpriteNode()

override func didMove(to view: SKView) {

physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx: 0.0, dy: -0.5)
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsBody?.categoryBitMask = Collider.wall
physicsBody?.contactTestBitMask = 0x0
physicsBody?.collisionBitMask = Collider.fish | Collider.food
self.backgroundColor = .white

//Water setup
water = SKSpriteNode(color: .blue, size: CGSize(width: frame.width, height: frame.height - 150))
water.position = CGPoint(x: 0, y: -75)
water.alpha = 0.3
addChild(water)
water.zPosition = 4

//Fish one
let fish = Fish(texture: nil, color: .black, size:CGSize(width: 20, height: 20))
addChild(fish)
fish.position = CGPoint(x: frame.midX-50, y: frame.minY + 100)
fish.zPosition = 5

fish.moveAround(within: water.frame)

//Fish two
let fish2 = Fish(texture: nil, color: .black, size:CGSize(width: 20, height: 20))
addChild(fish2)
fish2.position = CGPoint(x: frame.midX+50, y: frame.minY + 100)
fish2.zPosition = 5

fish2.moveAround(within: water.frame)

}


func feed(at position:CGPoint, with food:SKSpriteNode){

food.position = CGPoint(x: position.x, y: frame.size.height/2 - food.frame.size.height)
addChild(food)
}

//MARK: Food factory :)
func getFood()->SKSpriteNode{

let food = SKSpriteNode(color: .purple, size: CGSize(width: 10, height: 10))

food.physicsBody = SKPhysicsBody(rectangleOf: food.frame.size)
food.physicsBody?.affectedByGravity = true
food.physicsBody?.categoryBitMask = Collider.food
food.physicsBody?.contactTestBitMask = Collider.fish
food.physicsBody?.collisionBitMask = Collider.wall
food.physicsBody?.linearDamping = (0.1 ... 0.95).random()
food.name = "food"
return food
}

//MARK: Feeding
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

if let touch = touches.first {

let location = touch.location(in: self)

let food = getFood()
feed(at: location, with: food)
}
}

//MARK: Eating
func didBegin(_ contact: SKPhysicsContact) {


guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else {

//Silliness like removing a node from a node tree before physics simulation is done will trigger this error
fatalError("Physics body without its node detected!")
}

let mask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

switch mask {

//Contact between fish and a food
case Collider.fish | Collider.food:

if let food = (contact.bodyA.categoryBitMask == Collider.food ? nodeA : nodeB) as? SKSpriteNode
{
self.nodesForRemoval.append(food)
}

default:
//some unknown contact occurred
break
}
}

//MARK: Removing unneeded nodes
override func didSimulatePhysics() {

for node in self.nodesForRemoval {
node.removeFromParent()
}
self.nodesForRemoval.removeAll()
}

//MARK: Chasing the food
override func update(_ currentTime: TimeInterval) {

self.enumerateChildNodes(withName: "fish") {
[unowned self] node, stop in

if let fish = node as? Fish {

self.enumerateChildNodes(withName: "food") {
node, stop in

fish.lock(food: node as! SKSpriteNode)
}

fish.chase(within: self.water.frame)
}
}
}
}

就是这样。在这里,我们设置我们的节点,解决接触检测并告诉哪些鱼应该追逐哪个食物节点。我留下了评论,所以一切都在那里。我希望这些方法几乎是不言自明的,但是您当然可以向我询问有关其工作原理的详细信息。

这里是一个简短的视频,展示了它是如何工作的:

enter image description here

和更长的版本,因为我只能上传两个 2 兆字节:screen recording

基本上,如果食物节点不在它定义的范围内,鱼是不会追逐它的。尽管如此,鱼还是会追逐锁定的节点,直到它吃掉为止。但如果附近有其他食物,鱼就会追逐那个食物节点。当然这不是必须的,您可以根据需要调整它(检查 isChasing:)方法。

关于ios - 以一定速度在屏幕上随机移动物体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44176202/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com