Physics body as an SKTexture does not rotate - xcode

I am making a game where the main character rotates, however, I want to use an SKTexture to set custom boundaries to the character sprite node. However, when I do this, my character does not rotate. When I use a regular circleOfRadius (because my sprite is round, but no perfectly round) it rotates, but is not accurate on collisions. Here is my code:
var mainSpriteTexture = SKTexture(imageNamed: "Main")
mainSprite = SKSpriteNode(texture: mainSpriteTexture)
mainSprite.setScale(0.2)
mainSprite.position = CGPoint(x: self.frame.width / 2, y: 100)
mainSprite.physicsBody = SKPhysicsBody(texture: mainSpriteTexture, size: mainSprite.size)
mainSprite.physicsBody?.categoryBitMask = PhysicsCatagory.player
mainSprite.physicsBody?.collisionBitMask = PhysicsCatagory.platform | PhysicsCatagory.ground
mainSprite.physicsBody?.contactTestBitMask = PhysicsCatagory.platform | PhysicsCatagory.ground | PhysicsCatagory.score
mainSprite.physicsBody?.affectedByGravity = true
mainSprite.physicsBody?.dynamic = true
mainSprite.physicsBody?.allowsRotation = true
self.addChild(mainSprite)
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
updateSpritePosition()
if gameStarted == true {
if died == false {
mainSprite.physicsBody?.velocity = CGVectorMake(direction, (mainSprite.physicsBody?.velocity.dy)!)
}
else if died == true {
mainSprite.physicsBody?.velocity = CGVectorMake(0, (mainSprite.physicsBody?.velocity.dy)!)
}
}
Here is the shape I am using: http://imgur.com/JCeEAbv

Not really sure how you rotate your sprite, or move it, but if I give it a certain default rotation and drop it on the surface, it rotates for me as it should (I used a picture you have provided). This way, it will not land perfectly on the ground (zRotation 0.0).
Try to use this:
mainSprite.zRotation = 0.1 //note that these are radians
and drop it on a flat surface.
Same thing you could probably achieve by applying a small force or impulse which should affect sprite's zRotation property... What you are going to use, depends on how you move your node actually - using physics, or by changing its position property directly (see my comment about mixing physics & manual node movement for important details).

Related

Animate CALayer - zoom and scroll

I want to create an animation with CALayers.
I have a parent layer with multiple sublayers and I would like to zoom in and scroll.
First I trying to zoom on the parent layer, as follows:
let transformAnimation = CABasicAnimation(keyPath: "bounds.size.width")
transformAnimation.duration = 2.3
transformAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transformAnimation.toValue = 650*2
transformAnimation.beginTime = CACurrentMediaTime() + 4
transformAnimation.autoreverses = false
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
parentLayer.add(transformAnimation, forKey: "transformAnimation")
//
let transformAnimation2 = CABasicAnimation(keyPath: "bounds.size.height")
transformAnimation2.duration = 2.3
transformAnimation2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transformAnimation2.toValue = 650*2 //CGAffineTransform.identity
transformAnimation2.beginTime = CACurrentMediaTime() + 4
transformAnimation2.autoreverses = false
transformAnimation2.isRemovedOnCompletion = false
transformAnimation2.fillMode = kCAFillModeForwards
parentLayer.add(transformAnimation2, forKey: "transformAnimation2")
When the animation is applied, the sublayers are left in wrong position and size. Should I also apply the animation to them?
How can I do this?
Thanks!
I'm guessing that you're updating width and height of one layer and you're wondering whether the bounds of sublayers will also change. No, you'll probably have to animate those separately, too. But rather than initiating separate animations, you can them with a CAAnimationGroup.
If these were views, you could define constraints that coordinate the resizing of subviews more gracefully. But with layers, you're going to have to do this yourself. (There might be reasons why you're doing it the way you are, but it's not clear from the question.)

How to enable MKMapView 3D view?

I have an MKMapView in a window, and pitchEnabled is true (and I've confirmed this in the debugger). The "3D" thingy in the middle of the compass is grayed out, and clicking or dragging it does nothing. Option-dragging the map (like I do in Maps.app) doesn't do anything, either.
From my interpretation of the docs, setting pitchEnabled should let me use the 3D view, like Maps.app does. Am I mistaken? Is there something else I need to do to allow my users to get a 3D map view?
You can get close to the experience of 3D mode by adjusting the camera angle from which you view the map and making buildings visible. The example below allows you view the Eiffel Tower in 3D:
viewDidLoad() {
super.viewDidLoad()
mapView.mapType = MKMapType.Standard
mapView.showsBuildings = true // displays buildings
let eiffelTowerCoordinates = CLLocationCoordinate2DMake(48.85815, 2.29452)
mapView.region = MKCoordinateRegionMakeWithDistance(eiffelTowerCoordinates, 1000, 100) // sets the visible region of the map
// create a 3D Camera
let mapCamera = MKMapCamera()
mapCamera.centerCoordinate = eiffelTowerCoordinates
mapCamera.pitch = 45
mapCamera.altitude = 500 // example altitude
mapCamera.heading = 45
// set the camera property
mapView.camera = mapCamera
}
example from: this question
Since OS X El Capitan v10.11 they added a new map type: "3D flyover mode"
For some reason this option doesn't show up in XCode attributes inspector of the mapview. You have to set it programmatically. This makes the map look and behave as the one seen in the maps app.
self.mapView.mapType = MKMapTypeSatelliteFlyover;
I was able to do this in swift on iOS 11:
mapView.mapType = .hybridFlyover
That is giving me the 3D view.
Use this setup method to configure you MapView in viewDidLoad.
func setup() {
objMapView.showsUserLocation = true
objMapView.delegate = self
objMapView.showsBuildings = true
objMapView.mapType = .hybridFlyover
objLocationManager.startUpdatingLocation()
if let center = self.objLocationManager.location?.coordinate {
let currentLocationCoordinates = CLLocationCoordinate2DMake(center.latitude, center.longitude)
objMapView.region = MKCoordinateRegion.init(center: currentLocationCoordinates, latitudinalMeters: 1000, longitudinalMeters: 100)
// create a 3D Camera
let mapCamera = MKMapCamera()
mapCamera.centerCoordinate = currentLocationCoordinates
mapCamera.pitch = 45
mapCamera.altitude = 100
mapCamera.heading = 45
// set the camera property
objMapView.camera = mapCamera
}
}
In above code snippet:
mapType property hybridFlyover displays satellite map along with location names if available.
MkMapCamera instance helps in creating 3D view of map. altitude property determine altitude from where location is to be projected.
Check below screenshot for output:

How would I create a "wall" on the left and right side of my screen Swift Spritekit?

I want objects to be able to pass through the bottom and top of the screen but not be able to leave the right and left side. I have this code but when my objects go to the left or right it doesn't act as a wall and lets it go right through it. I used the contactTestBitMask to make sure there was contact but there is no collision. This is the code for the wall on the left:
func wall() {
let leftWall = SKNode()
leftWall.physicsBody = SKPhysicsBody(edgeLoopFromRect:CGRectMake(1.0,
1.0, 1.0, CGRectGetHeight(self.frame)));
leftWall.physicsBody?.categoryBitMask = OutsideCategory
leftWall.physicsBody?.contactTestBitMask = HeroCategory
leftWall.physicsBody?.collisionBitMask = 0
leftWall.physicsBody?.mass = 5
leftWall.physicsBody?.friction = 1.0
leftWall.physicsBody?.restitution = 1
leftWall.physicsBody?.usesPreciseCollisionDetection = true
leftWall.physicsBody?.dynamic = false
leftWall.physicsBody?.affectedByGravity = false
leftWall.position = CGPointMake(0, self.size.height / 40)
leftWall.setScale(1.0)
self.addChild(leftWall)
As long as the category bit mask is set on your wall, and your walls do in fact exist where you expect them to exist (you can set a flag on the scene to draw physics bodies which will display your wall), and the collisionBitMask of the nodes that should collide with the walls are set to the categoryBitMask of the wall (and you have created world physics body on the scene), you should be good to go.
Example:
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
self.physicsBody.categoryBitMask = CNPhysicsCategoryEdge;
_catNode.physicsBody.collisionBitMask = CNPhysicsCategoryBlock | CNPhysicsCategoryEdge | CNPhysicsCategorySpring;
_catNode.physicsBody.contactTestBitMask = CNPhysicsCategoryBed | CNPhysicsCategoryEdge;
The cat sprite node in this example will collide with the world physics body now.
Your collisionBitMask cannot be set to 0. It must match an enum value that coinsides with the categoryBitMask of the other objects. Per apple's documentation:
collisionBitMask Property A mask that defines which categories of
physics bodies can collide with this physics body.
Declaration
SWIFT var collisionBitMask: UInt32 OBJECTIVE-C
#property(nonatomic, assign) uint32_t collisionBitMask
Discussion When
two physics bodies contact each other, a collision may occur. This
body’s collision mask is compared to the other body’s category mask by
performing a logical AND operation. If the result is a nonzero value,
this body is affected by the collision. Each body independently
chooses whether it wants to be affected by the other body. For
example, you might use this to avoid collision calculations that would
make negligible changes to a body’s velocity.
The default value is 0xFFFFFFFF (all bits set).
Example:
CGRect bodyRect = CGRectInset(frame, 2, 2);
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bodyRect.size];
self.physicsBody.categoryBitMask = CNPhysicsCategoryBlock;
self.physicsBody.collisionBitMask = CNPhysicsCategoryBlock | CNPhysicsCategoryCat | CNPhysicsCategoryEdge;

affectedbygravity in SceneKit?

I want to make a floating ball.
In spritekit I do physicsbody.affectedbygravity=NO;
But what have I do in scenekit?
I tried to set mass to zero, but dynamicbody turns to static: it not moved by gravity, nor by collision. My code is
// add ball
SCNNode *ball = [SCNNode node];
ball.geometry = [SCNSphere sphereWithRadius:5];
ball.geometry.firstMaterial.locksAmbientWithDiffuse = YES;
ball.geometry.firstMaterial.diffuse.contents = #"ball.jpg";
ball.geometry.firstMaterial.diffuse.contentsTransform = SCNMatrix4MakeScale(2, 1, 1);
ball.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeMirror;
ball.physicsBody = [SCNPhysicsBody dynamicBody];
ball.physicsBody.restitution = 0.9;
//ball.physicsBody.mass=0; //here makes a ball statical and not moving
ball.physicsBody.allowsResting = YES;
[[scene rootNode] addChildNode:ball];
UPD this not helps too - ball falls slowly.
- (void)renderer:(id<SCNSceneRenderer>)aRenderer didSimulatePhysicsAtTime:(NSTimeInterval)time{
[ball.physicsBody applyForce:SCNVector3Make(0, 9.8, 0) atPosition:SCNVector3Make(0,0,0) impulse:NO]; //this
ball.physicsBody.velocity=SCNVector3Make(0,0,0); //or this
}
You can turn off gravity by setting scene.physicsWorld.gravity to zero.
However If you want some objects to be affected and some not you need to add a [SCNPhysicsField linearGravityField] and configure the categoryBitMask to make it affect the nodes you want.
I found partial solution and some fun bug.
This makes ball stay unmovable until collision and "float" after that
// .... the same code above in creation
ball.physicsBody.mass=1;
[[scene rootNode] addChildNode:ball];
ball.physicsBody.mass=0; //important right after creating!!! or it become bodyless after changing mass to 1
and release it if contact. If you apply up force equal to gravity, it will be floating, slowly falling down. Speed of falling may be damped, of cause.
// physics contact delegate
- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact{
if (contact.nodeA.physicsBody.type == SCNPhysicsBodyTypeDynamic && contact.nodeA.physicsBody.mass==0)
contact.nodeA.physicsBody.mass=1;
else
if (contact.nodeB.physicsBody.type == SCNPhysicsBodyTypeDynamic && contact.nodeB.physicsBody.mass==0)
contact.nodeB.physicsBody.mass=1;
}

detect touch on GUI Textures

I have 2 gui textures.
According to screen width and height i kept it in gui.
One for joystick and another for shooter.
Now touching on shooter joystick moves to that specific portion.
i used rect.Contains.
void Start () {
xx = Screen.width - Screen.width/12;
yy = Screen.height - Screen.height/8;
lb = Screen.width/10;
rect = new Rect(-xx/2, -yy/2, lb, lb);
shooter.pixelInset = rect;
shooter.enabled = false;
}
void OnGUI(){
if(characterScript.playbool){
shooter.enabled = true;
}
if (rect.Contains(Event.current.mousePosition)){
shootBool = true;
print("shoot");
alert.text="shoot";
}
}
Not working properly for me. Think space coordinates are different from gui coordinates. How can fix this problem.do anyone can suggest any other good method
You can try HitTest.
function Update()
{
for (var touch : Touch in Input.touches)
{
if (rect.Contains(touch.position))
{
// we are now in the guitexture 'rect'
Debug.Log("rect touched");
exit;
}
}
}
The above code is used with touches, as you described in your question. However, since you tagged mouse, I don't know for sure if you use the mouse or a touch.
So, if you use the mouse to click on the object, you can use:
if (rect.Contains(Input.mousePosition) && Input.GetMouseButtonDown(0))
{
Debug.Log("rect clicked");
exit;
}
Some debugging of your code brings out a few problems.
Say the screen resolution is 800x600px:
xx = 800 - 800 / 12
= 733.3333333333333
yy = 600 - 600 / 8
= 525
lb = 800 / 10
= 80
rect = (-366.665, -262.5, 80, 80)
Why is lb determined by the screen width divided by ten?
Now the heart of the problem is that Event.mousePosition starts at the top left of the screen, while GUITexture.pixelInset is based in the center of the screen.
You will have to either adjust the Event.mousePosition to use the center of the screen as the origin, or you will have to adjust the rect variable to start at the top-left of the screen.

Resources