CGRectIntersectsRect at a specific point of the objects? - ios

Like in a Mario game where if you jump and land on top of a monster, the monster gets knocked out.
I'm using CGRectIntersectsRect between the two objects (player and monster); however, the monster will get knocked out from any direction.
How do I intersect two objects at specific points of the objects?
I actually created separate blank objects in each direction for this to work. Is there a more efficient solution?

Instead of CGRectIntersectsRect you can use CGRectIntersection to get a new CGRect of the area of intersection. If the player hit from the side, then this rectangle will be taller than it is wide. If the player hit from the top the rectangle will be wider than it is tall, but you will need to check for top vs. bottom maybe. In that case, you can compare the Y values of the enemy rectangle with the intersection. If the player hit from the top, the Y values of the intersection and the enemy rectangle will be equal. Otherwise, the player hit from the bottom.

typedef NS_ENUM(NSUInteger, IntersectFrom) {
IntersectFromNotIntersect,
IntersectFromTop,
IntersectFromBottom,
IntersectFromLeft,
IntersectFromRight
};
IntersectFrom CGRectGetIntersectFrom(CGRect rectPlayer, CGRect rectMonster) {
CGRect rectIntersect = CGRectIntersection(rectPlayer, rectMonster);
if(!CGRectIsNull(rectIntersect)) {
if(CGRectGetWidth(rectIntersect) < CGRectGetHeight(rectIntersect)) {
// LEFT or RIGHT
if(CGRectGetMinX(rectIntersect) == CGRectGetMinX(rectMonster)) {
return IntersectFromLeft;
}
else {
return IntersectFromRight;
}
}
else {
// TOP or BOTTOM
if(CGRectGetMinY(rectIntersect) == CGRectGetMinY(rectMonster)) {
return IntersectFromBottom;
}
else {
return IntersectFromTop;
}
}
}
else {
return IntersectFromNotIntersect;
}
}

Related

How to place an object at specific location in ARcore?

I am trying to place an object at specific location without tapping, Is there any direct method to place an object at real world location.
Certainly you could do this.
Search flat surface.
2.override onUpdate method.
loop through trackables and than your model has been projected without tapping.
i just give you an idea, how onUpdate method is look like, this is pseudo code
frame = arFragment.getArSceneView().getArFrame();
if (frame != null) {
//get the trackables
Iterator<Plane> var3 = frame.getUpdatedTrackables(Plane.class).iterator();
while (var3.hasNext()) {
Plane plane = var3.next();
//If a plane has been detected & is being tracked by ARCore
if (plane.getTrackingState() == TrackingState.TRACKING) {
//Hide the plane discovery helper animation
arFragment.getPlaneDiscoveryController().hide();
//Get all added anchors to the frame
Iterator<Anchor> iterableAnchor = frame.getUpdatedAnchors().iterator();
//place the first object only if no previous anchors were added
if (!iterableAnchor.hasNext()) {
//Perform a hit test at the center of the screen to place an object without tapping
List<HitResult> hitTest = frame.hitTest(getScreenVector3().x, getScreenVector3().y);
//iterate through all hits
Iterator<HitResult> hitTestIterator = hitTest.iterator();
while (hitTestIterator.hasNext()) {
HitResult hitResult = hitTestIterator.next();
//Create an anchor at the plane hit
Anchor modelAnchor = plane.createAnchor(hitResult.getHitPose());
//Attach a node to this anchor with the scene as the parent
anchorNode = new AnchorNode(modelAnchor);
anchorNode.setParent(arFragment.getArSceneView().getScene());
**create a new TranformableNode that will carry our model**
}
}
}
}
}
}
You can add the node directly to the scene:
node.localPosition = Vector3(x, y, z)
arSceneView.scene.addChild(node)

Borders Function not stopping sprites from getting out

First of you should know, the area where the player is allowed to play in is a square, that is 320x320 which is centered in the screen. That 320x320 square is a 5x5 grid [64x64 each tile], and the player is always centered in the tiles of the grid.
When the player moves, no matter the direction, its always a 64 pixel move.[1/5 of the grid's size].
func move(node: SKSpriteNode, direction: String) {
//'node' determines what spritekitnode to move.
//'direction' classifies which statement to run.
if node.name == "player" {
if direction == "left" {
if node.position.x - 64 >= map.position.x - 160 {
//Checks if the Node can move without
//moving off the grid [map, the 5x5 grid].
node.runAction(moveLeft)
//Moves the Node
}
}
if direction == "right" {
if node.position.x + 64 <= map.position.x + 160 {
node.runAction(moveRight)
}
}
if direction == "up" {
if node.position.y + 64 <= map.position.y + 160 {
node.runAction(moveUp)
}
}
if direction == "down" {
if node.position.y - 64 >= map.position.y - 160 {
node.runAction(moveDown)
}
}
if direction == nil {
print("[move() function] : No direction specified!")
}
}
}
My problem, is when the player spams or quickly presses the button to move multiple times
consecutively, the player ends up being able to move off the grid.
Example, when the player is on the bottom tile of the 5x5 grid, and moves down 2 times fast
enough, the player goes off the grid.
This is because RunAction does not remove the previous action, so when they spam, they queue up multiple actions to run concurrently. You need to either have your move functions not allow the bounds to be broken, or call removeAllActions before you run a new action

How do I detect if a number is a decreasing or increasing number? iOS

I'm using a SpriteKit game engine within XCode while developing a game that bounces a ball and platforms come from the sky and the objective is to bounce on the platforms to get higher. I need to add a velocity to the ball when it falls down + comes in contact with a platform. I'm having trouble trying to detect the balls Y position. I had something like this in the update method but nothing happens... I'm open to suggestions.
//The value of the _number instance variable is the Y position of the ball.
if (_number++) {
NSLog(#"UP");
}
if (_number--) {
NSLog(#"DOWN");
}
You can see a node's position by using code like this:
if(myNode.position.y > 0)
NSLog(#"y is greater than 0");
If you want to check a node's current speed (vector) you can do it like this:
if(myNode.physicsBody.velocity.dy > 0)
NSLog(#"Moving up");
if(myNode.physicsBody.velocity.dy < 0)
NSLog(#"Moving down");
Remember that position and speed (vector) are not the same thing.
You need to read the SKPhysicsBody docs to understand about CGVector and other important issues.
Alright, this:
if (_number++) {
NSLog(#"UP");
}
if (_number--) {
NSLog(#"DOWN");
}
doesn't work to do anything. It shouldn't even run. I don't why it doesn't give an error.
number++; is a simplified form of
number = number + 1;
You can't place it in an if statement.
First make the variable #property int ypos in your header.
Then every update compare the current y to the ypos.
After comparing, save the current y to ypos.
Place this in your Update method.
if (ball.position.y > _ypos)
{
//going up
}
else if (ball.position.y < _ypos)
{
//going down
}
_ypos = ball.position.y;
P.S. I haven't had access to a computer so the formatting might suck from my phone

What to do to improve this jumping code?

I have this iOS game where the frog jumps from lily pad to lily pad. The game is in the vertical orientation, and the perspective I'm trying to achieve is an aerial view, where the user looks at the frog and scenery from the top. The frog jumps from lily pad to lily pad and the code is the following:
Bounce Method:
-(void)bounce {
[self jumpSound];
if (frog.center.y > 450) {
upMovement = 2;
}
else if (frog.center.y > 350) {
upMovement = 1.5;
}
else if (frog.center.y > 250) {
upMovement = 0.5;
}
}
and the lilypad movement:
-(void)lilyPadMovement {
if (CGRectIntersectsRect(frog.frame, lily.frame) && (upMovement <=-1)){
[self bounce];
[self lilyPadFall];
if (lilyPadUsed == NO) {
addedScore = 1;
lilyPadUsed = YES;
}
}
}
Essentially what I'm trying to fix is the frogs bouncing movement. When the frog lands in the middle of a lily pad and bounces it doesn't look bad, but at times the frog will simply touch the sides of the lil pad and the bounce method will be called because the rectangles intersected. I tried CGRectContainsRect but it made the game to hard, because it delayed the speed of the game. So I'm not sure how to fix it. Any suggestions?
You really want to know if there's a sufficient overlap to enable the frog to push off, rather than fall in the water. So maybe the distance between the centers has to be less than X, where you tune X based on what looks good.
For example ( using the Pythagorean formula) :
//having defined this function:
inline float square (float a) { return a*a; }
//change intersect test to:
if (((square(frog.center.x-lily.center.x) + square(frog.center.y-lily.center.y)) < 10000) &&
(upMovement <= -1)) { ...

How can I perform clipping on rotated rectangles?

So I have this Panel class. It's a little like a Window where you can resize, close, add buttons, sliders, etc. Much like the status screen in Morrowind if any of you remember. The behavior I want is that when a sprite is outside of the panel's bounds it doesn't get drawn and if it's partially outside only the part inside gets drawn.
So what it does right now is first get a rectangle that represents the bounds of the panel, and a rectangle for the sprite, it finds the rectangle of intersection between the two then translates that intersection to the local coordinates of the sprite rectangle and uses that for the source rectangle. It works and as clever as I feel the code is I can't shake the feeling that there's a better way to do this. Also, with this set up I cannot utilize a global transformation matrix for my 2D camera, everything in the "world" must be passed a camera argument to draw. Anyway, here's the code I have:
for the Intersection:
public static Rectangle? Intersection(Rectangle rectangle1, Rectangle rectangle2)
{
if (rectangle1.Intersects(rectangle2))
{
if (rectangle1.Contains(rectangle2))
{
return rectangle2;
}
else if (rectangle2.Contains(rectangle1))
{
return rectangle1;
}
else
{
int x = Math.Max(rectangle1.Left, rectangle2.Left);
int y = Math.Max(rectangle1.Top, rectangle2.Top);
int height = Math.Min(rectangle1.Bottom, rectangle2.Bottom) - Math.Max(rectangle1.Top, rectangle2.Top);
int width = Math.Min(rectangle1.Right, rectangle2.Right) - Math.Max(rectangle1.Left, rectangle2.Left);
return new Rectangle(x, y, width, height);
}
}
else
{
return null;
}
}
and for actually drawing on the panel:
public void DrawOnPanel(IDraw sprite, SpriteBatch spriteBatch)
{
Rectangle panelRectangle = new Rectangle(
(int)_position.X,
(int)_position.Y,
_width,
_height);
Rectangle drawRectangle = new Rectangle();
drawRectangle.X = (int)sprite.Position.X;
drawRectangle.Y = (int)sprite.Position.Y;
drawRectangle.Width = sprite.Width;
drawRectangle.Height = sprite.Height;
if (panelRectangle.Contains(drawRectangle))
{
sprite.Draw(
spriteBatch,
drawRectangle,
null);
}
else if (Intersection(panelRectangle, drawRectangle) == null)
{
return;
}
else if (Intersection(panelRectangle, drawRectangle).HasValue)
{
Rectangle intersection = Intersection(panelRectangle, drawRectangle).Value;
if (Intersection(panelRectangle, drawRectangle) == drawRectangle)
{
sprite.Draw(spriteBatch, intersection, intersection);
}
else
{
sprite.Draw(
spriteBatch,
intersection,
new Rectangle(
intersection.X - drawRectangle.X,
intersection.Y - drawRectangle.Y,
intersection.Width,
intersection.Height));
}
}
}
So I guess my question is, is there a better way to do this?
Update: Just found out about the ScissorRectangle property. This seems like a decent way to do this; it requires a RasterizerState object to be made and passed into the spritebatch.Begin overload that accepts it. Seems like this might be the best bet though. There's also the Viewport which I can apparently change around. Thoughts? :)
There are several ways to limit drawing to a portion of the screen. If the area is rectangular (which seems to be the case here), you could set the viewport (see GraphicsDevice) to the panel's surface.
For non-rectangular areas, you can use the stencil buffer or use some tricks with the depth buffer. Draw the shape of the surface in the stencil buffer or the depth buffer, set your render state to draw only pixels located in the shape you just rendered in the stencil/depth buffer, finally render your sprites.
One way of doing this is simple per-pixel collision. Although this is a bad idea if the sprites are large or numerous, this can be a very easy and fast way to get the job done with small sprites. First, do a bounding circle or bounding square collision check against the panel to see if you even need to do per-pixel detection.
Then, create a contains method that checks if the position, scale, and rotation of the sprite put it so far inside the panel that it must be totally enclosed by the panel, so you don't need per-pixel collision in that case. This can be done pretty easily by just creating a bounding square that has the width and height of the length of the sprite's diagonal, and checking for collision with that.
Finally, if both of these fail, we must do per-pixel collision. Go through and check against every pixel in the sprite to see if it is within the bounds of the panel. If it isn't set the alpha value of the pixel to 0.
Thats it.

Resources