gpt4 book ai didi

swift - 使用 enumerateBodies alongRayStart 反弹光线

转载 作者:搜寻专家 更新时间:2023-10-31 22:39:10 24 4
gpt4 key购买 nike

我想在我的 SpriteKit GameScene 中追踪子弹移动的路径。我正在使用“enumerateBodies(alongRayStart)”,我可以很容易地计算出与物理体的第一次碰撞。

给定接触点和接触法线,我不知道如何计算反射角。

我想计算超过 5 次反射/反弹的路径,所以首先我:

  1. 转换一条射线,获取与其相交的所有物体,并获取最近的物体。
  2. 然后我使用该接触点作为我下一次反射/反弹的开始....但我正在为应该将终点设置为什么而苦苦挣扎......

认为我应该做的是获取接触点和接触法线之间的角度,然后计算与之相反的新点...

    var points: [CGPoint] = []
var start: CGPoint = renderComponent.node.position
var end: CGPoint = crossHairComponent.node.position

points.append(start)

var closestNormal: CGVector = .zero

for i in 0...5 {

closestNormal = .zero
var closestLength: CGFloat? = nil
var closestContact: CGPoint!

// Get the closest contact point.
self.physicsWorld.enumerateBodies(alongRayStart: start, end: end) { (physicsBody, contactPoint, contactNormal, stop) in

let len = start.distance(point: contactPoint)

if closestContact == nil {
closestNormal = contactNormal
closestLength = len
closestContact = contactPoint
} else {
if len <= closestLength! {
closestLength = len
closestNormal = contactNormal
closestContact = contactPoint
}
}
}


// This is where the code is just plain wrong and my math fails me.
if closestContact != nil {

// Calculate intersection angle...doesn't seem right?
let v1: CGVector = (end - start).normalized().toCGVector()
let v2: CGVector = closestNormal.normalized()
var angle = acos(v1.dot(v2)) * (180 / .pi)

let v1perp = CGVector(dx: -v1.dy, dy: v1.dx)
if(v2.dot(v1perp) > 0) {
angle = 360.0 - angle
}

angle = angle.degreesToRadians


// Set the new start point
start = closestContact

// Calculate a new end point somewhere in the distance to cast a ray to, so we can repeat the process again
let x = closestContact.x + cos(angle)*100
let y = closestContact.y + sin(-angle)*100
end = CGPoint(x: x, y: y)

// Add points to array to draw them on the screen
points.append(closestContact)
points.append(end)
}

}

最佳答案

我猜你正在寻找这样的东西吧?

enter image description here

1。工作代码

首先让我发布完整的工作代码。只需创建一个基于 SpriteKit 的新 Xcode 项目和

  1. 在 GameViewController.swift 中设置

    scene.scaleMode = .resizeFill

  2. 删除您在 GameScene.sks

  3. 中找到的常用标签
  4. 用以下代码替换Scene.swift

>

import SpriteKit

class GameScene: SKScene {

override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}

var angle: CGFloat = 0

override func update(_ currentTime: TimeInterval) {
removeAllChildren()
drawRayCasting(angle: angle)
angle += 0.001
}

private func drawRayCasting(angle: CGFloat) {
let colors: [UIColor] = [.red, .green, .blue, .orange, .white]

var start: CGPoint = .zero
var direction: CGVector = CGVector(angle: angle)

for i in 0...4 {
guard let result = rayCast(start: start, direction: direction) else { return }
let vector = CGVector(from: start, to: result.destination)

// draw
drawVector(point: start, vector: vector, color: colors[i])

// prepare for next iteration
start = result.destination
direction = vector.normalized().bounced(withNormal: result.normal.normalized()).normalized()
}
}

private func rayCast(start: CGPoint, direction: CGVector) -> (destination:CGPoint, normal: CGVector)? {

let endVector = CGVector(
dx: start.x + direction.normalized().dx * 4000,
dy: start.y + direction.normalized().dy * 4000
)

let endPoint = CGPoint(x: endVector.dx, y: endVector.dy)

var closestPoint: CGPoint?
var normal: CGVector?

physicsWorld.enumerateBodies(alongRayStart: start, end: endPoint) {
(physicsBody:SKPhysicsBody,
point:CGPoint,
normalVector:CGVector,
stop:UnsafeMutablePointer<ObjCBool>) in

guard start.distanceTo(point) > 1 else {
return
}

guard let newClosestPoint = closestPoint else {
closestPoint = point
normal = normalVector
return
}

guard start.distanceTo(point) < start.distanceTo(newClosestPoint) else {
return
}

normal = normalVector
}
guard let p = closestPoint, let n = normal else { return nil }
return (p, n)
}

private func drawVector(point: CGPoint, vector: CGVector, color: SKColor) {

let start = point
let destX = (start.x + vector.dx)
let destY = (start.y + vector.dy)
let to = CGPoint(x: destX, y: destY)

let path = CGMutablePath()
path.move(to: start)
path.addLine(to: to)
path.closeSubpath()
let line = SKShapeNode(path: path)
line.strokeColor = color
line.lineWidth = 6
addChild(line)
}
}


extension CGVector {

init(angle: CGFloat) {
self.init(dx: cos(angle), dy: sin(angle))
}

func normalized() -> CGVector {
let len = length()
return len>0 ? self / len : CGVector.zero
}

func length() -> CGFloat {
return sqrt(dx*dx + dy*dy)
}

static func / (vector: CGVector, scalar: CGFloat) -> CGVector {
return CGVector(dx: vector.dx / scalar, dy: vector.dy / scalar)
}

func bounced(withNormal normal: CGVector) -> CGVector {
let dotProduct = self.normalized() * normal.normalized()
let dx = self.dx - 2 * (dotProduct) * normal.dx
let dy = self.dy - 2 * (dotProduct) * normal.dy
return CGVector(dx: dx, dy: dy)
}

init(from:CGPoint, to:CGPoint) {
self = CGVector(dx: to.x - from.x, dy: to.y - from.y)
}

static func * (left: CGVector, right: CGVector) -> CGFloat {
return (left.dx * right.dx) + (left.dy * right.dy)
}

}

extension CGPoint {

func length() -> CGFloat {
return sqrt(x*x + y*y)
}

func distanceTo(_ point: CGPoint) -> CGFloat {
return (self - point).length()
}

static func - (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x - right.x, y: left.y - right.y)
}

}

2。它是如何工作的?

让我们看看这段代码做了什么。我们将从底部开始。

3。 CGPointCGVector 扩展

这些只是简单的扩展(主要取自 Ray Wenderlich's repository on GitHub )以简化我们将要执行的几何操作。

4。 drawVector(点:矢量:颜色)

这是一种使用给定的颜色从给定的开始绘制向量的简单方法。

这里没什么特别的。

private func drawVector(point: CGPoint, vector: CGVector, color: SKColor) {

let start = point
let destX = (start.x + vector.dx)
let destY = (start.y + vector.dy)
let to = CGPoint(x: destX, y: destY)

let path = CGMutablePath()
path.move(to: start)
path.addLine(to: to)
path.closeSubpath()
let line = SKShapeNode(path: path)
line.strokeColor = color
line.lineWidth = 6
addChild(line)
}

5。 rayCast(start:direction) -> (destination:CGPoint, normal: CGVector)?

此方法执行光线转换并返回光线进入与物理体碰撞的 ALMOST 最近点。

private func rayCast(start: CGPoint, direction: CGVector) -> (destination:CGPoint, normal: CGVector)? {

let endVector = CGVector(
dx: start.x + direction.normalized().dx * 4000,
dy: start.y + direction.normalized().dy * 4000
)

let endPoint = CGPoint(x: endVector.dx, y: endVector.dy)

var closestPoint: CGPoint?
var normal: CGVector?

physicsWorld.enumerateBodies(alongRayStart: start, end: endPoint) {
(physicsBody:SKPhysicsBody,
point:CGPoint,
normalVector:CGVector,
stop:UnsafeMutablePointer<ObjCBool>) in

guard start.distanceTo(point) > 1 else {
return
}

guard let newClosestPoint = closestPoint else {
closestPoint = point
normal = normalVector
return
}

guard start.distanceTo(point) < start.distanceTo(newClosestPoint) else {
return
}

normal = normalVector
}
guard let p = closestPoint, let n = normal else { return nil }
return (p, n)
}

几乎是衣橱是什么意思?

表示终点距离起点至少1点

guard start.distanceTo(point) > 1 else {
return
}

好的,但为什么呢?

因为没有这个规则,光线会卡在物理物体中,永远无法离开它。

6。 drawRayCasting(角度)

此方法基本上使局部变量保持最新以正确生成 5 个段。

private func drawRayCasting(angle: CGFloat) {
let colors: [UIColor] = [.red, .green, .blue, .orange, .white]

var start: CGPoint = .zero
var direction: CGVector = CGVector(angle: angle)

for i in 0...4 {
guard let result = rayCast(start: start, direction: direction) else { return }
let vector = CGVector(from: start, to: result.destination)

// draw
drawVector(point: start, vector: vector, color: colors[i])

// prepare next direction
start = result.destination
direction = vector.normalized().bounced(withNormal: result.normal.normalized()).normalized()
}
}

第一段的起点为零,方向与角度参数相差。

段 2 到 5 使用最后一点和前一段的“镜像方向”。

更新(_ 当前时间:TimeInterval)

这里我只是在每一帧调用 drawRayCasting 传递当前角度值和增加的角度 0.001。

var angle: CGFloat = 0
override func update(_ currentTime: TimeInterval) {
removeAllChildren()
drawRayCasting(angle: angle)
angle += 0.001
}

6。 didMove(查看:SKView)

最后,我在场景周围创建了一个物理体,以使光线在边界上反弹。

override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}

7.总结

我希望解释清楚。如果您有任何疑问,请告诉我。

更新

退回函数中存在错误。它妨碍了对反射光线的正确计算。现在已修复。

enter image description here

关于swift - 使用 enumerateBodies alongRayStart 反弹光线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51819678/

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