TouchesMoved is lagged in swift - swift

I am writing the code in Xcode 6 Beta 6 using Swift in SpriteKit. In the code I need a picture to follow the finger when the user moves it. touchesMoved works but with glitches. If I move the finger slowly everything is fine. If I the move the finger fast going right to left then everything is fine. If I move the finger fast going left to right, then the picture follows the finger only for a fraction of a second. If I tap and hold the picture in its current position for about half a second then everything is fine when I move it fast going both from right to left or left to right. In summary I cannot move the picture fast going left to right unless I tap and hold the picture for about half a second. Anybody has a clue why this is happening? Thanks for your time. Below is the code. I am moving SKSPriteNode follow2
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let angle_area_location = touch.locationInNode(self)
if self.nodeAtPoint(angle_area_location) == self.angle_area {
if (angle_area_location.x <= 21) {
angle = 1.55681122463001
distance12 = sqrt((angle_area_location.y - 30) * (angle_area_location.y - 30) + 1)
}
if (angle_area_location.y <= 31) {
angle = 0.0102037274939542
distance12 = sqrt((31 - 30) * (31 - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20))
}
if (angle_area_location.x > 21) && (angle_area_location.y > 31) {
angle = atan((angle_area_location.y - 30) / (angle_area_location.x - 20))
distance12 = sqrt((angle_area_location.y - 30) * (angle_area_location.y - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20))
}
if (distance12 <= maxFollow2) && (distance12 >= minFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
if (angle_area_location.x > 21) || (angle_area_location.y > 31) {
follow2.position = CGPointMake(angle_area_location.x , angle_area_location.y)
}
if(angle_area_location.x <= 21) {
follow2.position = CGPointMake(21 , angle_area_location.y)
}
if (angle_area_location.y <= 31) {
follow2.position = CGPointMake(angle_area_location.x , 31)
}
}
if(distance12 > maxFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
delta = 290/3
arc.size = CGSizeMake(160 * (1 + delta/20) , 35)
arc.position = CGPointMake(20 - 3 * (delta) * cos(angle) , 30 - 3 * (delta) * sin(angle))
followdist = 360
follow2.position = CGPointMake(angle_area_location.x , angle_area_location.y)
velocity = vmin + (followdist - minFollow2) * (300/(maxFollow2 - minFollow2))
}
if (distance12 < minFollow2) {
self.cannon.zRotation = angle
self.arc.zRotation = angle
arc.size = CGSizeMake(160 , 6.8)
arc.position = CGPointMake(20 , 30)
follow2.position = CGPointMake( minFollow2 * cos(angle) + 20 , minFollow2 * sin(angle) + 30)
followdist = sqrt((follow2.position.y - 30) * (follow2.position.y - 30) + (follow2.position.x - 20) * (follow2.position.x - 20))
velocity = vmin + (followdist - minFollow2) * (300/(maxFollow2 - minFollow2))
}
}
}
}

Ok I figured the glitch. I had a UISwipeGestureRecognizer that calls a method when I right swipe. I deactivated that and everything works fine. I guess swiping right and moving left to right on touchesMoved are interfering with each other.

A common trick when working with distances is to avoid taking the square root and only compare the squared values. This saves quite a bit of processor resources.
Example:
let maxfollow2sqr = maxFollow2 * maxFollow2
distance12 = (angle_area_location.y - 30) * (angle_area_location.y - 30) + (angle_area_location.x - 20) * (angle_area_location.x - 20)
if (distance12 <= maxFollow2sqr) {
// do something here
}
Since all you care about is if the calculated distance is between the min and the max you can deal just with the squares. This may speed up the function quite a bit but there are probably other optimizations that can be done.

Related

In scene kit, SCNPhysicsField.noiseField seems to just terminate, end, after a few seconds

Have an SCNNode, add a physics body,
physicsBody = SCNPhysicsBody(type: .dynamic, shape: ..)
physicsBody?.isAffectedByGravity = false
Now add a physics field, for example
physicsField = SCNPhysicsField.noiseField(smoothness: 0.2, animationSpeed: 0.01)
physicsField?.strength = 0.05
It works perfectly. In this case, .noise , the object will jiggle around.
However after a few seconds (often 7 seconds, sometimes a different length of time), the object will simply stop moving.
(The three values, smoothness speed and strength, make no difference if you change them - it will still end after a few seconds.)
What's the solution to this mystery?
Just to be clear, I never used a SCNPhysicsField.noiseField, but I used one of type SCNPhysicsField.linearGravity and another of type SCNPhysicsField.customField and both of them are working correctly and do not stop unexpected as you describe.
here are my examples:
let attractionField = SCNPhysicsField.linearGravity()
attractionField.halfExtent = SCNVector3(250.0, 35.0, 60.0)
attractionField.direction = SCNVector3(-1.0, 0.0, 0.0)
attractionField.strength = 0.2 // 0.15
attractionNode.physicsField = attractionField
and the other one, (which I used to create a tornado):
private func addCustomVortexField() {
// Tornado Particles Field
let worldOrigin = stormNode.presentation.worldPosition
let worldAxis = simd_float3(0.0, 1.0, 0.0)
let customVortexField = SCNPhysicsField.customField(evaluationBlock: { position, velocity, mass, charge, time in
let l = simd_float3(worldOrigin.x - position.x, 1.0, worldOrigin.z - position.z)
let t = simd_cross(worldAxis, l)
let d2: Float = l.x * l.x + l.z * l.z
let vs: Float = 27 / sqrt(d2) // diameter, the bigger the value the wider it becomes
let fy: Float = 1.0 - Float((min(1.0, (position.y / 240.0)))) // rotations, a higher value means more turn arounds (more screwed)
return SCNVector3Make(t.x * vs + l.x * 10 * fy, 0, t.z * vs + l.z * 10 * fy)
})
customVortexField.halfExtent = SCNVector3Make(100, 100, 100)
stormNode.physicsField = customVortexField
stormNode.physicsField?.categoryBitMask = BitMasks.BitmaskTornadoField
}
I hope this is gonna help you in some way. You can also provide me your project, and I will have a look at it.

Determine if angle is close to zero by +/- 2?

In my app, I'm trying to provide an indication that the user's heading is within a reasonable range from zero. The angle resets to zero if it goes beyond 360. So this is what I'm doing:
let angle1 = Angle(degree: 359)
let angle2 = Angle(degree: 2)
angle1.degrees > 358 || angle1.degrees < 2
angle2.degrees > 358 || angle2.degrees < 2
Is there a built in method or better way to test for this? Also, could there be a scenario where the CoreLocation heading is larger than 360 or a negative number?
What you're doing works, but there is a way to do this in code that I suppose is a more direct/accurate code representation of the logic. That would be to check whether a range of values contains your value. In this case, we're checking whether the ranges 0 to 2 or 358 to 360 contain the relevant current angle in degrees.
As a side note, I like to wrap these sorts of calculations into my models as a computed property. It might look something like...
struct Angle {
var degrees: Double
var isCloseToZero: Bool {
let upperRange = 358.0...360.0
let lowerRange = 0.0...2.0
return lowerRange.contains(degrees) || upperRange.contains(degrees)
}
}
This can be accessed like...
let angle = Angle(degrees: 359)
let isClsoeToZero = angle.isCloseToZero
you could try the following code to achieve what you asked.
var angle = Angle(degrees: 359.0) // Angle(degrees: 2.0)
// between 358 and 360 inclusive or between 0 and +\- 2 inclusive
if angle.degrees >= 358 && angle.degrees <= 360 ||
angle.degrees >= 0 && angle.degrees <= 2 ||
angle.degrees >= -2 && angle.degrees <= 0 {
print("Yes, user is heading within a reasonable range from zero")
}
Regarding CoreLocation course (heading), according to the docs,
https://developer.apple.com/documentation/corelocation/cllocation/1423832-course
"... A negative value indicates that the course information is invalid..."
could there be a scenario where the CoreLocation heading is larger
I'm not sure, but lots of angle-related math might take your value out of the 0..<360 range. To remedy this, you can add make something like:
extension Angle {
// Return a normalized copy of this angle that's guaranteed to be within +0 ..< +360
var normalized: Angle {
let potentiallyNegativeAngle = degrees.truncatingRemainder(dividingBy: 360.0)
let positiveInRangeAngle = (potentiallyNegativeAngle + 360).truncatingRemainder(dividingBy: 360.0)
return Angle(degrees: positiveInRangeAngle) }
}
Is there a built in method or better way to test for this?
No, but it can be pretty fun to write your own. Here's how I would do it:
// It astounds me that these basic operators aren't already built-in
extension Angle {
static func + (minuend: Angle, subtrahend: Angle) -> Angle {
Angle(radians: minuend.radians + subtrahend.radians)
}
static func - (minuend: Angle, subtrahend: Angle) -> Angle {
Angle(radians: minuend.radians - subtrahend.radians)
}
}
extension Angle {
// There's probably some clever way to do this without branching,
// and purely with modular arithmetic, but I couldn't figure it out
func isWithin(_ delta: Angle, of target: Angle) -> Bool {
return self.normalized > (target - delta).normalized ||
self.normalized < (target + delta).normalized
}
func isCloseToZero(delta: Angle = Angle(degrees: 2.0)) -> Bool {
isWithin(delta, of: Angle(degrees: 0))
}
}
Here are some test cases:
print(" -340.0: ", Angle(degrees: -340.0).isCloseToZero()) // False
print(" -358.1: ", Angle(degrees: -358.1).isCloseToZero())
print(" -5.0: ", Angle(degrees: -5.0).isCloseToZero()) // False
print(" -1.9: ", Angle(degrees: -1.9).isCloseToZero())
print(" 0.0: ", Angle(degrees: 0.0).isCloseToZero())
print(" +1.9: ", Angle(degrees: +1.9).isCloseToZero())
print(" +5.0: ", Angle(degrees: +5.0).isCloseToZero()) // False
print(" +358.1: ", Angle(degrees: +358.1).isCloseToZero())
print(" +360.0: ", Angle(degrees: +360.0).isCloseToZero())
print(" +365.0: ", Angle(degrees: +365.0).isCloseToZero()) // False
Here's a fancier variant, which does this totally branch-free using some clever modular arithmetic:
extension Angle {
// Returns the distance between `self` and `target`, in the range `-180..<180` degrees
func distance(to target: Angle) -> Angle {
let rawDistance = (self - target).radians
let normalizedDistance = .pi - abs(abs(rawDistance) - .pi)
return Angle(radians: normalizedDistance)
}
func isWithin(_ delta: Angle, of target: Angle) -> Bool {
let normalizedDelta = delta.normalized
precondition(normalizedDelta.radians <= .pi,
"""
`isWithin(_:of:)` always find the shortest distance between the two angles,
so the delta has to be congruent to an angle between 0 and 180 degrees!
It was \(delta), which normalized to: \(normalizedDelta)
"""
)
return abs(self.distance(to: target).radians) <= normalizedDelta.radians
}
func isCloseToZero(delta: Angle = Angle(degrees: 2.0)) -> Bool {
isWithin(delta, of: Angle(degrees: 0))
}
}

GetAxis("Mouse X/Y") does not work with virtual mouse

I recently tried writing a bot in python for a unity game to control the character using a virtual mouse like in pyautogui or autohotkey. The game has multiple camera modes. One that uses the cursor location and another that uses GetAxis("Mouse X/Y"). Neither the library I'm using, pyautogui, pydirectinput, or even an autohotkey macro fails to move the mouse when on the option that use GetAxis() although it works on the camera that uses the cursor location. Why is this the case?
Example of a script that fails when using GetAxis():
Yoinked from another stack overflow post since I needed an ahk example as well:
CoordMode, mouse, screen
toggle := 0, fixedY := A_ScreenHeight/2 ; choose the y you like
F1::
MouseGetPos, MouseX, MouseY
if toggle := !toggle
gosub, MoveTheMouse
else
SetTimer, MoveTheMouse, off
return
MoveTheMouse:
Random, x, 1, % A_ScreenWidth
MouseMove, %x%, %fixedY%, 100
Random, Time, 1000*60, 1000*60*5
SetTimer, MoveTheMouse, -%time% ; every 3 seconds
return
The C# code used for movement is:
case CAMERA_TYPE.CAMERA_THAT_WORKS_WITH_BOT:
if (Input.mousePosition.x < (float)Screen.width * 0.4f)
{
mainT.RotateAround(mainT.position, Vector3.up, -(((float)Screen.width * 0.4f - Input.mousePosition.x) / (float)Screen.width * 0.4f) * getSensitivityMultiWithDeltaTime() * 150f);
}
else if (Input.mousePosition.x > (float)Screen.width * 0.6f)
{
mainT.RotateAround(mainT.position, Vector3.up, (Input.mousePosition.x - (float)Screen.width * 0.6f) / (float)Screen.width * 0.4f * getSensitivityMultiWithDeltaTime() * 150f);
}
mainT.rotation = Quaternion.Euler(140f * ((float)Screen.height * 0.6f - Input.mousePosition.y) / (float)Screen.height * 0.5f, mainT.rotation.eulerAngles.y, mainT.rotation.eulerAngles.z);
mainT.position -= mainT.forward * this.distance * this.distanceMulti * this.distanceOffsetMulti;
break;
case CAMERA_TYPE.CAMERA_THAT_FAILS_WITH_BOT:
if (!CustomInputs.Inputs.menuOn)
{
Screen.lockCursor = true;
}
float num5 =0f;
float num6 =0f;
if (((int)GameManager.settings[300]) == 0)
{
num5 = (Input.GetAxis("Mouse X") * 10f) * this.getSensitivityMulti();
num6 = ((-Input.GetAxis("Mouse Y") * 10f) * this.getSensitivityMulti()) * this.getReverse();
}
else if (((int)GameManager.settings[300]) == 1)
{
num5 = (Input.GetAxisRaw("Mouse X") * 10f) * this.getSensitivityMulti();
num6 = ((-Input.GetAxisRaw("Mouse Y") * 10f) * this.getSensitivityMulti()) * this.getReverse();
}
mainT.RotateAround(mainT.position, Vector3.up, num5);
float num7 = mainT.rotation.eulerAngles.x % 360f;
float num8 = num7 + num6;
if (((num6 <= 0f) || (((num7 >= 260f) || (num8 <= 260f)) && ((num7 >= 80f) || (num8 <= 80f)))) && ((num6 >= 0f) || (((num7 <= 280f) || (num8 >= 280f)) && ((num7 <= 100f) || (num8 >= 100f)))))
{
mainT.RotateAround(mainT.position, mainT.right, num6);
}
mainT.position -= (Vector3)(((mainT.forward * this.distance) * this.distanceMulti) * this.distanceOffsetMulti);
break;
Some of this code is incomprehensible due to being decompiled. Also a note, don't worry about modding here as the game has basically been abandoned by the dev for 4 years and is now running on community servers.
After some further searching I found that GetAxis may use the mouse velocity/acceleration and I don't think these mouse move functions affect that in any way. In my use case what I would need for testing is a way to read the mouse acceleration/velocity and a way to move set my mouse's acceleration/velocity. Please let me know if I have the right idea here and any references to material related to this would be greatly appreciated.
The fix I found was to use a windows dll call:
AHK:
DllCall("mouse_event", "UInt", 0x01, "UInt", 100, "UInt", 100)
python:
import ctypes
ctypes.windll.user32.mouse_event(0x01, x, y, 0, 0)

Angle evaluation in swift, strange behavior when angle close to 360

i am having a trouble when evaluating and comparing some angle.
i have create this function which return a Color base on some condition
i'm passing to the function to parameter which are startingHeading and currentHeading
func lh1(startHeading:Double, currentHeading : Double)->Color {
let HDGstart : Angle = Angle(degrees: startHeading)
let HDHCurrent : Angle = Angle(degrees: currentHeading)
var coloreDaMostrare : Color = .black
if HDHCurrent >= HDGstart - Angle(degrees: 10) && HDHCurrent < HDGstart - Angle(degrees: 5) {
coloreDaMostrare = .orange
}
else if HDHCurrent > HDGstart - Angle(degrees: 5){
coloreDaMostrare = .black
}
else if HDHCurrent < HDGstart - Angle(degrees: 10) {
coloreDaMostrare = .orange
}
return coloreDaMostrare
}
it work fine when the startingHeading is above 10 deg, but when I'm close to 0/360 it get crazy because the math HDHStart - 10 give a negative angle.
for swift 2 deg - 3 result in -1 but with angle should be 359...
what I'm doing wrong?
is there any way can fix this issue, I have seen few post online the suggest to use modulo-operator .. I have try but swift game warning say can not use in swift ( and to be honest I don't understand how it work)
looking for some help..
thanks a lot
The following function should allow for input angles both smaller and larger than 0...360 to be converted into that range:
func convertToPositiveAngle(_ angle : Angle) -> Angle {
var toRet = Angle(degrees: fmod(angle.degrees, 360))
if toRet.degrees < 0 {
toRet.degrees += 360.0;
}
return toRet
}
The fmod function is the floating point equivalent of the modulus operator -- it tells you the remainder, basically. So, fmod(370.5, 360) should give you 10.5.
In your example code, every time you do the subtraction, you should use the above equation. So:
if HDHCurrent >= convertToPositiveAngle(HDGstart - Angle(degrees: 10)) && HDHCurrent < convertToPositiveAngle(HDGstart - Angle(degrees: 5)) {
etc

Moving a Camera in SpriteKit advanced

I want to make a camera like in the video bellow
// Please watch the video first :)
https://www.youtube.com/watch?v=nryuCql2k9A
I try this
// moveWorld is true when the game lunch
if(moveWorld){
moveWorld = false
rightHeroBorder = hero.position.x + (hero.frame.size.width * 2)
leftHeroBorder = hero.position.x - (hero.frame.size.width * 2)
}
// move world to the right
if(hero.position.x > rightHeroBorder){
let diff = hero.position.x - rightHeroBorder
rightHeroBorder = hero.position.x + 1
leftHeroBorder = hero.position.x - 1
self.world.position.x -= diff
}
// move world to the left
if(hero.position.x < leftHeroBorder){
let diff = leftHeroBorder - hero.position.x
leftHeroBorder = hero.position.x - 1
rightHeroBorder = hero.position.x + 1
self.world.position.x += diff
}
The result is not what I want
in the beginning the world don't move
but when the node passes rightHeroBorder or leftHeroBorder it starts to move
If the node move a little, the world will move (doesnt detects the borders)
if the node continue to move to the right, less of the right part of screen is visible (this should be opposite)
This is hard, but I don't want to center the node and the world.
Hopefully people with more experience will help
I'am asking about x coordinate system, but you can help with the y as well