I have this game where i need to know if the ball has hit a wall on the side (to bounce back on the x-axis) or on the top (to bounce back on the y-axis, like a bounce on the ground). They work fine individually, but when I uncomment both of them, it dosen't work. (I think this is because the code is 'overlapping'?). Anyway, here is the code, and any help is fantastic:
if (CGRectIntersectsRect(guy.frame, wall_01.frame)) {
if (guy.frame.origin.y+guy.frame.size.height >= wall_01.frame.origin.y && guy.frame.origin.y <= wall_01.frame.origin.y+wall_01.frame.size.height) {
iJump *= kRestitution;
}
if (guy.frame.origin.x+guy.frame.size.width >= wall_01.frame.origin.x && guy.frame.origin.x <= wall_01.frame.origin.x+wall_01.frame.size.width) {
jJump *= kRestitution;
}
}
assuming wall is on the left side and the y increases from top to bottom
CGFloat leftWall = someXPosition;
CGFloat ground = someYPosition;
CGFloat ballLeft = CGRectGetMinX(guy.frame);
CGFloat ballRight = CGRectGetMaxX(guy.frame);
CGFloat ballBottom = CGRectGetMaxY(guy.frame);
if (ballLeft <= leftwall && ballBot >= ground){
//ball hit corner ?
} else if (ballLeft <= leftWall){
//passed or touched wall
} else if (ballBot >= ground){
//passed or touched ground
}
Related
I'm working on a simple puzzle-style Unity game for iOS. The game presents the user a "rubiks cube" object (one large cube comprised of smaller cubes).
I want users to be able to swipe left/right/up/down on the cube to rotate as expected.
My code works, but after the user performs a 2nd rotation, the cube does not rotate according to the user's expected direction. After the first rotation, the x/y/z axis have rotated along with the device.
If I only use the left/right swipe code, it works as expected. When I add up/down swiping, it breaks. Which makes sense, since the axis doesn't rotate when I stick to two variables.
Here is my code:
if (Input.GetMouseButtonUp(0))
{
//swipe upwards
if (currentSwipe.y > 0 && currentSwipe.x > -1f && currentSwipe.x < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 0f, 90f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe down
if (currentSwipe.y < 0 && currentSwipe.x > -1f && currentSwipe.x < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 0f, -90f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe left
if (currentSwipe.x < 0 && currentSwipe.y > -1f && currentSwipe.y < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, -90f, 0f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe right
if (currentSwipe.x > 0 && currentSwipe.y > -1f && currentSwipe.y < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 90f, 0f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
}
Any help is appreciated! I'm relatively new to Unity so still learning.
I think it is easier (at least for me) to rethink the setup like this:
private Quaternion _targetRotation; // this will jump to the rotation in discrete steps (i.e. not gradually)
Start(){
_targetRotation = cube.transform.localRotation;
....
}
...
var appliedRotation = Quaterion.identity;
//swipe upwards
if (currentSwipe.y > 0 && currentSwipe.x > -1f && currentSwipe.x < 1f){
appliedRotation = Quaternion.AngleAxis(90f, Vector3.right);
}
... // other swipe cases
_targetRotation = appliedRotation * _targetRotation; // order is important afaik
...
gameObject.transform.DOLocalRotateQuaternion(_targetRotation);
let me know if this works.
I want to create an eyeball that follows the user. In this sense, the eye should follow the position of the user but should only be able to move within certain bounds (The eye socket). The code I wrote below works, but its very choppy.
if(dist <= socketRadius - self.aEye.size.width/2.2){
lastPosition = self.aEye.position
self.aEye.physicsBody?.velocity = CGVector(dx:theVec.dx * eyeMoveSpeed, dy:theVec.dy * eyeMoveSpeed)
}
else{
let toCenterVector = normalizeVector( CGVector(dx:(self.socket.position.x - self.aEye.position.x), dy:(self.socket.position.y - self.aEye.position.y)*3 ))
self.aEye.physicsBody?.velocity = toCenterVector
print(toCenterVector)
print("Out")
}
(dist is the distance from the eyeball to the eyesocket center)
Is there a way to have a smooth flow of the eyeball around its socket to follow the user's position?
You can use SKConstraints to achieve this. Something like:
let rangeToCenterSprite = SKRange(lowerLimit: 80, upperLimit: 90)
var distanceConstraint: SKConstraint
distanceConstraint = SKConstraint.distance(rangeToCenterSprite, toNode: SpriteNodeInEyeCenter)
EyeBallSprite.constraints = [distanceConstraint]
So I have this Swift code using SpriteKit:
let ballX = ball.position.x
let ballY = ball.position.y
let ballR = ball.size.width / 2
let sceneW = self.size.width
let sceneH = self.size.height
if ballY >= sceneH - ballR - 3 {
framesOnTop += 1
} else {
framesOnTop = 0
}
if framesOnTop > 2 {
// Some code here
}
I have the ball node (SKSpriteNode) that can hit the ceiling and bounce back (scene has no gravity) but sometimes it can hit the ceiling at a very steep angle and get stuck in an infinite loop, left right left right left right. In the if statement that says framesOnTop > 2 I am successfully detecting when it gets stuck, and I want to make it bounce back down at the right angle with physicsBody.applyImpulse. But to do this I need the velocity from before it gets stuck (~5 frames before). Is this possible or is there a better way to fix it?
I am trying to have my Label to increment +1 every time a sprite makes contact with a Contact node, but it increments by large numbers like +23. I think what is happening is that its taking into account every millisecond that the sprite is touching the node, but i don't know how to fix it.
Here is my Label node code within DidMoveToView:
score = 0
scoreLabelNode = SKLabelNode(fontNamed:"[z] Arista Light")
scoreLabelNode.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/4)
scoreLabelNode.zPosition = 100
scoreLabelNode.fontSize = 500
scoreLabelNode.alpha = 0.03
scoreLabelNode.text = String(score)
self.addChild(scoreLabelNode)
here is my contact node:
var contactNode = SKNode()
contactNode.position = CGPoint(x: self.frame.size.width + asteroidTexture.size().height / 15 + rocket.size.width, y: 0.0)
contactNode.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake( asteroid.size.width/16, self.frame.size.height*2 ))
contactNode.physicsBody?.dynamic = false
contactNode.physicsBody?.categoryBitMask = scoreCategory
contactNode.physicsBody?.contactTestBitMask = rocketCategory
contactNode.runAction(asteroidsMoveAndRemove)
moving.addChild(contactNode)
and here is my code where when my rocket sprite makes contact with the contactNode it increments:
func didBeginContact(contact: SKPhysicsContact) {
if moving.speed > 0 {
if ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory {
// Rocket has contact with score entity
score++
scoreLabelNode.text = String(score)
println("HIT")
}
else{
gameOver()
}
}
'moving' is when my asteroid sprites are moving, the contact nodes move with it
It's probably not every millisecond, but instead every frame, and that's why you likely end up with numbers like +23 (The collision is taking place for a 1/3 of a second and if it's running at 60 FPS that is about 20 or so frames).
One thing you could do is subclass SKNode into an actual ContactNode class that has a property hasBeenStruck that is initially set to false or something to that extent.
Then when you check contact, you first check to see if hasBeenStruck is false. If it is, register the hit and update the score and then set that ContactNode's hasBeenStruck property to true. This way, on the next 20 or so checks to see if contact has happened, it won't keep updating the label because the if condition has failed.
However, if your game allows a user to hit the same ContactNode twice and get score both times, this isn't a complete solution. If that is the case, you could use a timer or "invincibility" period for the given ContactNode.
I am trying to run a "Walk" style animation on my main game sprite. The animations work fine, and my sprite is hooked up to my joystick all fine and dandy.
However, I think where I setup the call for my walk animations are wrong. Because everytime the sprite is moving, the animation stops.
I know putting the animation in such a weak if statement is probably bad, but please tell me how I could get my sprite to animate properly while it is being moved by the joystick.
The sprite faces the right direction, so I can tell the action's first frame is being called, however, it doesn't animate until I stop touching my joystick.
Here is How I call the action:
//WALK LEFT
if (joypadCap.position.x <= 69 /*&& joypadCap.position.y < && joypadCap.position.y > >40 */ )
{
[tjSprite runAction:walkLeft];
};
//WALK RIGHT
if ( joypadCap.position.x >= 71 /* && joypadCap.position.y < 100 && >joypadCap.position.y > 40 */)
{
[tjSprite runAction:walkRight];
};
THIS: is the how the joystick controls the character:
CGPoint newLocation = ccp(tjSprite.position.x - distance/8 * cosf(touchAngle),
tjSprite.position.y - distance/8 * sinf(touchAngle));
tjSprite.position = newLocation;
Please help. Any alternative ways to call the characters walk animation would be greatly appreciated!
int current_state;
if (current_state != 1 && joypadCap.position.x <= 69)
{
current_state = 1;
[tjSprite runAction:walkLeft];
}
else if (current_state != 1 && joypadCap.position.x >= 71)
{
current_state = 1;
[tjSprite runAction:walkRight];
}
else
{
current_state = 0;
//[tjSprite stopAllActions];
};
The sprite faces the right direction,
so I can tell the action's first frame
is being called, however, it doesn't
animate until I stop touching my
joystick.
Based on the code you have supplied this actually makes sense. What your if statement is saying is anytime the joypadCap position is greater than 71 or less than 69 play an animation. This means your animations will try to play over and over again from the beginning everytime joypadCap's position falls in these ranges. I assume joypadCap is a measure of how much the joystick is being pressed?
Looks like you could use some additional state logic to determine what your character should be doing. Here is some pseudocode...
state current_state;
if (current_state != walking and joypadCap.position.x <= 69)
{
current_state = walking;
[tjSprite runAction:walkLeft];
}
else if (current_state != walking and joypadCap.position.x >= 71)
{
current_state = walking;
[tjSprite runAction:walkRight];
}
else
{
current_state = idle;
[tjSprite stopAllActions];
}
Keep in mind that is loose pseudocode. Not everything is syntactically correct but logically the idea is that you have a state variable to keep track of the characters current state which allows you so have your animations play one time only. Let me know if this helps and if you have any questions about my answer.