xcode stop a sprite from going off screen cocos2d - xcode

im using the below code but that doesnt seem to be stopping the sprite from going off screen even though the code builds. can anyone tell me how it needs to be changed so that when the sprite gets to the edge of the screen along the x coordinate it stops.
-(void)applyJoystick:(SneakyJoystick *)aJoystick toNode:(CCNode *)tempNode forTimeDelta:(float)deltaTime
{
CGPoint scaledVelocity = ccpMult(aJoystick.velocity, 1024.0f);
CGPoint newPosition = ccp(tempNode.position.x + scaledVelocity.x * deltaTime, tempNode.position.y);
CGSize screenSize = [CCDirector sharedDirector].winSize;
CGFloat spriteWidth = vikingSprite.contentSize.width;
CGFloat x = tempNode.position.x + scaledVelocity.x * deltaTime;
if (x < 0 + (spriteWidth/2)) {
x = 0 + (spriteWidth/2);
} else if (x > screenSize.width - (spriteWidth/2)) {
x = screenSize.width - (spriteWidth/2);
}
[tempNode setPosition:newPosition];
if (jumpButton.active == YES) {
CCLOG(#"Jump button is pressed.");
}
if (attackButton.active == YES) {
CCLOG(#"Attack button is pressed.");
}
}
thanks

You never re-assign the changed variable x back to your vikingsprite after you modified it.
So you need to do something like:
if (x < 0 + (spriteWidth/2)) {
x = 0 + (spriteWidth/2);
} else if (x > screenSize.width - (spriteWidth/2)) {
x = screenSize.width - (spriteWidth/2);
}
CGPoint vikingPos = cpp(x, vikingsprite.position.y);
[vikingsprite setPosition:vikingPos];
My syntax could be wrong I don't write a lot of objective-c, but I use cocos2d-x which is the C++ version of cocos2d

Set edge as boundary.
boundary.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
Don't forget to send Ray Wenderlich a beer.

I think your forgetting to reset it's position?
if (x < 0 + (spriteWidth/2)) {
x = 0 + (spriteWidth/2);
tempNode.position.x = x;
} else if (x > screenSize.width - (spriteWidth/2)) {
x = screenSize.width - (spriteWidth/2);
tempNode.position.x = x;
}

Related

Raylib 2D & Go - Do something when a collision ends

new to Raylib and trying to create a Super Mario clone using Go. I am using rl.CheckCollisionRecs to detect collision between the player and a pipe object, which is using the AABB collision method to detect if a player is hitting the X or Y axis first. The problem I am facing is that when the player collides with the Y, the player's Y position is set to match the pipes y position plus its height as expected. However, the player does not fall back to the ground when they leave the collider. How can I code an action when the rectangles are now longer colliding?
When they player leaves the collider, I want them to fall back to the ground.
func drawColliders() {
for _, current_Pipe := range pipes {
rl.DrawRectangle(current_Pipe.posX, current_Pipe.posY, current_Pipe.width, current_Pipe.height, current_Pipe.Color)
if rl.CheckCollisionRecs(playerDest, rl.NewRectangle(float32(current_Pipe.posX), float32(current_Pipe.posY), float32(current_Pipe.width), float32(current_Pipe.height))) {
var xDistance float32
var yDistance float32
var dx float32
var dy float32
if playerDest.X < float32(current_Pipe.posX) {
dx = float32(current_Pipe.posX) - playerDest.Width
isColliding = true
} else if playerDest.X > float32(current_Pipe.posX-current_Pipe.width) {
dx = float32(current_Pipe.posX) + float32(current_Pipe.width)
isColliding = true
}
if playerDest.Y < float32(current_Pipe.posY) {
dy = float32(current_Pipe.posY) - (playerDest.Y + playerDest.Height)
isColliding = true
} else if playerDest.Y > float32(current_Pipe.posY) {
dy = float32(current_Pipe.posY) + (float32(current_Pipe.posY) + float32(current_Pipe.height))
isColliding = true
}
xDistance = dx
yDistance = dy
// fmt.Println(xDistance, yDistance)
var xAxisTimeToCollide float32 = float32(math.Abs(float64(xDistance) / float64(velocityX)))
var yAxisTimeToCollide float32 = float32(math.Abs(float64(yDistance) / float64(velocityY)))
// fmt.Println("X Time: ", xAxisTimeToCollide, " | Y Time: ", yAxisTimeToCollide)
if xAxisTimeToCollide < yAxisTimeToCollide {
// fmt.Println("Collision on the X axis")
if playerDest.X < float32(current_Pipe.posX) {
playerDest.X = float32(current_Pipe.posX) - playerDest.Width
} else if playerDest.X > float32(current_Pipe.posX-current_Pipe.width) {
playerDest.X = float32(current_Pipe.posX) + float32(current_Pipe.width)
}
} else {
// fmt.Println("Collsion on the Y axis")
playerGrounded = true
playerJumping = false
velocityY = 0
playerDest.Y = float32(current_Pipe.posY) - playerDest.Height
}
}
}
Problem solved. CheckCollisionRecs is checking if there is a collision, but does not check when a collision ends.
My solution was that when a collision was detected, I stored the static objects starting coordinate and ending coordinate (startPoint = obj.positionX, endPoint = obj.positionX + obj.width). Then in my update function, I have an if statement that checks if the players current X position is more than or less than the static objects start or end point.

When creating an angle, how do I control the attributes of the automatically created points?

I'm working with a polygon and attempting to create angles with labels but when angles are created, so are the points used to define them. This would be fine but I can't control the labels on the automatically created points (and I don't know what they are called or how to find out).
var points = [
[0, 0],
[0, 5],
[3, 0]
];
for (k = 0; k < showAngle.length; k++) {
if (showAngle[k] == 1) {
var angle = board.create('angle', [points[k], points[((k + 1) % points.length)], points[((k + 2) % points.length)]],{fixed:true});
} else if (showAngle[k] == 2) {
var angle = board.create('angle', [points[k], points[((k + 1) % points.length)], points[((k + 2) % points.length)]], {
fixed: false,
name: function() {
return ((180/Math.PI)*JXG.Math.Geometry.rad(points[k], points[((k + 1) % points.length)], points[((k + 2) % points.length)])).toFixed(1) + '°';
}
});
}
}
https://jsfiddle.net/jscottuq/acyrLxfh/12/ contains what I've got so far.
The arrays showLen and showAngle are setting what labels are shown for each side/angle (0 - no label, 1 - name , 2 - measurement).
These will be set when the jsxgraph is created.
At the time being, the possibility to control the style of the newly created points of an angle is missing. We will add this soon.
However, a solution would be to use the already existing points which are hidden in this example. For this it would be helpful to kee a list of these points, e.g. jxg_points:
var jxg_points = [];
for (i = 0; i < points.length; i++) {
var rise = points[(i + 1) % points.length][1] - points[i][1];
var run = points[(i + 1) % points.length][0] - points[i][0];
var point = board.create('point', [points[i][0], points[i][1]], {
fixed: true,
visible:false
});
jxg_points.push(point); // Store the point
points[i].pop();
len[i] = Math.round((Math.sqrt(rise * rise + run * run) + Number.EPSILON) * 100) / 100;
}
Then the points can be reused for the angles without creating new points:
for (k = 0; k < showAngle.length; k++) {
if (showAngle[k] == 1) {
angle = board.create('angle', [
jxg_points[k],
jxg_points[((k + 1) % jxg_points.length)],
jxg_points[((k + 2) % jxg_points.length)]
],{fixed:true});
} else if (showAngle[k] == 2) {
var angle = board.create('angle', [
jxg_points[k],
jxg_points[((k + 1) % jxg_points.length)],
jxg_points[((k + 2) % jxg_points.length)]], {
fixed: false,
name: function() {
return ((180/Math.PI)*JXG.Math.Geometry.rad(points[k], points[((k + 1) % points.length)], points[((k + 2) % points.length)])).toFixed(1) + '°';
}
});
}
}
See it live at https://jsfiddle.net/d8an0epy/.

How to implement snapping effect and collision detection between two objects using Threejs?

We are able to detect the collision but could not implement a snapping/magnetic effect like Snap edges of objects to each other and prevent overlap
we need help with 3D objects here and we are using Vec3 for the active object's position.
With the following approach, collision detection is working perfectly for all cases, and magnetic effect is somehow working - not perfectly.
It's working well when the object is moving along x or z-axis but when the object's movement is in diagonal direction (moving along x and z-axis simultaneously) that is where the problem comes.
Though am not satisfied with the following approach that's why am looking for new approach to implement both magnetic and collision detection features.
It is not necessary to have the solution in Threejs, any general solution or algorithm of coordinates can be converted into Threejs.
let collide = this.detectCollisionCubes(activeObject, collidingObject, vec3);
let magneticEffect = new MagneticEffect(activeObject, vec3, collidingObject);
vec3 = magneticEffect.setNewPosition();
activeObject.position.copy(vec3);
detectCollisionCubes = function(a, d, vec3){
// a is active object's positon
// d is colliding object
let aHeight = Math.abs(a.getHeight());
let aWidth = Math.abs(a.getWidth());
let aDepth = Math.abs(a.getDepth());
let b1 = vec3.y - aHeight / 2;
let t1 = vec3.y + aHeight / 2;
let r1 = vec3.x + aWidth / 2;
let l1 = vec3.x - aWidth / 2;
let f1 = vec3.z - aDepth / 2;
let B1 = vec3.z + aDepth / 2;
let dHeight = Math.abs(d.getHeight());
let dWidth = Math.abs(d.getWidth());
let dDepth = Math.abs(d.getDepth());
let b2 = d.position.y - dHeight / 2;
let t2 = d.position.y + dHeight / 2;
let r2 = d.position.x + dWidth / 2;
let l2 = d.position.x - dWidth / 2;
let f2 = d.position.z - dDepth / 2;
let B2 = d.position.z + dDepth / 2;
if (t1 < b2 || r1 < l2 || b1 > t2 || l1 > r2 || f1 > B2 || B1 < f2) {
return false;
}
return true;
}
Trying to create magnetic effect via
this.currentObject = currentObject;
this.collisionObject = collisionObject;
this.collisionType = null;
this.objectType = null;
this.currentPosition = currentPosition;
this.currentObjectHeight = Math.abs(currentObject.getHeight());
this.currentObjectWidth = Math.abs(currentObject.getWidth());
this.collisionObjectHeight = Math.abs(collisionObject.getHeight());
this.collisionObjectWidth = Math.abs(collisionObject.getWidth());
this.collisionObjectDepth = Math.abs(collisionObject.getDepth());
this.objectTop = currentObject.position.y + (this.currentObjectHeight/2);
this.objectBottom = currentObject.position.y - (this.currentObjectHeight/2);
this.collideTop = collisionObject.position.y + (this.collisionObjectHeight/2);
this.collideBottom = collisionObject.position.y - (this.collisionObjectHeight/2);
this.zAxisDifference = Math.abs(Math.abs(currentPosition.z) - Math.abs(collisionObject.position.z));
this.xAxisDifference = Math.abs(Math.abs(currentPosition.x) - Math.abs(collisionObject.position.x));
// Extra code here
if (
this.objectTop < this.collideBottom
) {
this.collisionType = collisionTypes.verticalBottom;
} else if (
this.objectBottom > this.collideTop
) {
this.collisionType = collisionTypes.verticalTop;
} else if (
this.currentPosition.x > this.collisionObject.position.x &&
this.zAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalXLeft;
} else if (
this.currentPosition.x < this.collisionObject.position.x &&
this.zAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalXRight;
} else if (
this.currentPosition.z > this.collisionObject.position.z &&
this.xAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalZLeft;
} else if (
this.currentPosition.z < this.collisionObject.position.z &&
this.xAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalZRight;
}
MagneticEffect.prototype.setNewPosition = function () {
if (this.collisionType === collisionTypes.verticalBottom) {
this.currentPosition.y = this.collideBottom + 0.5;
} else if (this.collisionType === collisionTypes.verticalTop) {
this.currentPosition.y = this.collideTop - 0.5;
} else if (this.collisionType === collisionTypes.horizentalXRight) {
this.currentPosition.x = this.collisionObject.position.x - this.collisionObjectWidth - 0.5;
} else if (this.collisionType === collisionTypes.horizentalXLeft) {
this.currentPosition.x = this.collisionObject.position.x + this.collisionObjectWidth + 0.5;
} else if (this.collisionType === collisionTypes.horizentalZRight) {
this.currentPosition.z = this.collisionObject.position.z - this.collisionObjectWidth - 0.5;
} else if (this.collisionType === collisionTypes.horizentalZLeft) {
this.currentPosition.z = this.collisionObject.position.z + this.collisionObjectWidth + 0.5;
}
return this.currentPosition;
};

How to use a line draiwng algorithm with min/max? F#

Sorry if this question is not suited for this site, but I don't know where else too ask it. I'm pretty confused, I use min and max I believe correctly. When I move the weapon with arrow keys the line seem to works find when its moving right or down. If anybody knows how to fix it or just put me on the right track I'd greatly appreciate it. For anyone interested I am creating this program for a game which I need a line of sight in for the weapon.
EDIT:
I am trying to achieve a state where I can use w,a,s,d keys to move the C (character) and arrow keys to move W (weapon). Eventually when I add enemies the line between the character and weapon will be used to see if they are in range to be attacked. Like a gun shooting in any direction. But when I move C in it's current state the line does not connect to C anymore. I'm not sure why this is.
open System
let [<Literal>] CharacterN = ConsoleKey.W
let [<Literal>] CharacterE = ConsoleKey.D
let [<Literal>] CharacterS = ConsoleKey.S
let [<Literal>] CharacterW = ConsoleKey.A
let [<Literal>] WeaponN = ConsoleKey.UpArrow
let [<Literal>] WeaponE = ConsoleKey.RightArrow
let [<Literal>] WeaponS = ConsoleKey.DownArrow
let [<Literal>] WeaponW = ConsoleKey.LeftArrow
type Point =
{ X : int
Y : int }
let p1 = { X = 0; Y = 0 }
let p2 = { X = 10; Y = 10 }
let rec main p1 p2 =
Console.Clear()
let dx = min p1.X p2.X - max p1.X p2.X
let dy = min p1.Y p2.Y - max p1.Y p2.Y
for x in min p1.X p2.X .. max p1.X p2.X do
let y = min p1.X p2.X + dy * (x - min p1.X p2.X) / dx
Console.SetCursorPosition(x, y)
printf "."
Console.SetCursorPosition(p1.X, p1.Y)
printf "C"
Console.SetCursorPosition(p2.X, p2.Y)
printf "W"
match Console.ReadKey().Key with
| CharacterN -> main { X = p1.X; Y = p1.Y - 1 } p2
| CharacterE -> main { X = p1.X + 1; Y = p1.Y } p2
| CharacterS -> main { X = p1.X; Y = p1.Y + 1 } p2
| CharacterW -> main { X = p1.X - 1; Y = p1.Y } p2
| WeaponN -> main p1 { X = p2.X; Y = p2.Y - 1 }
| WeaponE -> main p1 { X = p2.X + 1; Y = p2.Y }
| WeaponS -> main p1 { X = p2.X; Y = p2.Y + 1 }
| WeaponW -> main p1 { X = p2.X - 1; Y = p2.Y }
| _ -> ()
main p1 p2
Console.Read() |> ignore
I believe that are several issues with your code. Probably the most important one is in the logic of the line:
let y = min p1.X p2.X + dy * (x - min p1.X p2.X) / dx
Obviously it should have been something like
y = y0 + (x-x0)*dy/dx
i.e. the first term shuold be something about Y rather than X. Unfortunately with your looping logic it should be the Y of the point that has the smaller X. It is not that easy to say that. IMHO it is easier to fix looping using negative step.
Another naive assumption is that you can always draw a line as having some y for each x. This is obviously not so when dy > dx. Moreover in case of a vertical line when dx is 0, the code will fail. The most most popular Bresenham's line algorithm requires you to handle those cases as explicitly different. Here is a simple implementation that handle those cases:
let drawLine p1 p2 =
let dx = p2.X - p1.X
let dy = p2.Y - p1.Y
if((dx <> 0) || (dy <> 0)) then
if abs(dx) >= abs(dy) then
let step = if (p1.X < p2.X) then 1 else -1
for x in p1.X ..step.. p2.X do
let y = p1.Y + dy * (x - p1.X) / dx
Console.SetCursorPosition(x, y)
printf "."
else
let step = if (p1.Y < p2.Y) then 1 else -1
for y in p1.Y .. step .. p2.Y do
let x = p1.X + dx * (y - p1.Y) / dy
Console.SetCursorPosition(x, y)
printf "."
As for other issues, you probably want to restrict positions of your points to some visible area between 0 and some max value. SetCursorPosition call with a negative value or a value larger than the buffer size will crash your app.

Algorithm - Given a set of pixels with coordinates, how to find all the contiguous lines in an efficient way?

I am working on an extrusion function to create a mesh given a 2D texture and the thickness of it.
Example:
I have achieved finding the outline of the texture by simply looking for the pixels either near the edge or near transparent ones. It works great even for concave (donut-shaped) shapes but now I am left with an array of outline pixels.
Here is the result:
The problem is that the values, by being ordered from top-left to bottom-right, they are not suitable for building an actual 3D outline.
My current idea is the following:
Step 1.
From index [0], look at the right-hand side for the nearest contiguous point different from the starting point.
If found, move it into another array.
If nothing, look at the bottom. Continue until the starting point has been reached.
Step2.
Pick another pixel, if any, from the pixels remained in the array.
Repeat from Step1.
This, in my head, would work but it seems quite inefficient. Researching, I found about the Moore-Neighbor tracing algorithm but I couldn't find anywhere an example where it worked with convex shapes.
Any thoughts?
At the end, I managed to find my own answer, so here I want to share it:
After finding the outline of a given image (using the alpha value of each pixel), the pixels will be ordered in rows, good for drawing them but bad for constructing a mesh.
So, the next step is to find contiguous lines. This is done by checking first if there are any neighbors to the found pixel giving priority to the ones top/left/right/bottom (otherwise it will skip the corners).
Keep going until no pixels are left in the original array.
Here is the actual implementation (for Babylon.js but the idea works with any other engine):
Playground: https://www.babylonjs-playground.com/#9GPMUY#11
var GetTextureOutline = function (data, keepOutline, keepOtherPixels) {
var not_outline = [];
var pixels_list = [];
for (var j = 0; j < data.length; j = j + 4) {
var alpha = data[j + 3];
var current_alpha_index = j + 3;
// Not Invisible
if (alpha != 0) {
var top_alpha = data[current_alpha_index - (canvasWidth * 4)];
var bottom_alpha = data[current_alpha_index + (canvasWidth * 4)];
var left_alpha = data[current_alpha_index - 4];
var right_alpha = data[current_alpha_index + 4];
if ((top_alpha === undefined || top_alpha == 0) ||
(bottom_alpha === undefined || bottom_alpha == 0) ||
(left_alpha === undefined || left_alpha == 0) ||
(right_alpha === undefined || right_alpha == 0)) {
pixels_list.push({
x: (j / 4) % canvasWidth,
y: parseInt((j / 4) / canvasWidth),
color: new BABYLON.Color3(data[j] / 255, data[j + 1] / 255, data[j + 2] / 255),
alpha: data[j + 3] / 255
});
if (!keepOutline) {
data[j] = 255;
data[j + 1] = 0;
data[j + 2] = 255;
}
} else if (!keepOtherPixels) {
not_outline.push(j);
}
}
}
// Remove not-outline pixels
for (var i = 0; i < not_outline.length; i++) {
if (!keepOtherPixels) {
data[not_outline[i]] = 0;
data[not_outline[i] + 1] = 0;
data[not_outline[i] + 2] = 0;
data[not_outline[i] + 3] = 0;
}
}
return pixels_list;
}
var ExtractLinesFromPixelsList = function (pixelsList, sortPixels) {
if (sortPixels) {
// Sort pixelsList
function sortY(a, b) {
if (a.y == b.y) return a.x - b.x;
return a.y - b.y;
}
pixelsList.sort(sortY);
}
var lines = [];
var line = [];
var pixelAdded = true;
var skipDiagonals = true;
line.push(pixelsList[0]);
pixelsList.splice(0, 1);
var countPixels = 0;
while (pixelsList.length != 0) {
if (!pixelAdded && !skipDiagonals) {
lines.push(line);
line = [];
line.push(pixelsList[0]);
pixelsList.splice(0, 1);
} else if (!pixelAdded) {
skipDiagonals = false;
}
pixelAdded = false;
for (var i = 0; i < pixelsList.length; i++) {
if ((skipDiagonals && (
line[line.length - 1].x + 1 == pixelsList[i].x && line[line.length - 1].y == pixelsList[i].y ||
line[line.length - 1].x - 1 == pixelsList[i].x && line[line.length - 1].y == pixelsList[i].y ||
line[line.length - 1].x == pixelsList[i].x && line[line.length - 1].y + 1 == pixelsList[i].y ||
line[line.length - 1].x == pixelsList[i].x && line[line.length - 1].y - 1 == pixelsList[i].y)) || (!skipDiagonals && (
line[line.length - 1].x + 1 == pixelsList[i].x && line[line.length - 1].y + 1 == pixelsList[i].y ||
line[line.length - 1].x + 1 == pixelsList[i].x && line[line.length - 1].y - 1 == pixelsList[i].y ||
line[line.length - 1].x - 1 == pixelsList[i].x && line[line.length - 1].y + 1 == pixelsList[i].y ||
line[line.length - 1].x - 1 == pixelsList[i].x && line[line.length - 1].y - 1 == pixelsList[i].y
))) {
line.push(pixelsList[i]);
pixelsList.splice(i, 1);
i--;
pixelAdded = true;
skipDiagonals = true;
}
}
}
lines.push(line);
return lines;
}
Algorithm Looping over pixels, we only check each pixel once, skipping empty cells, and store it in a list as there won't be duplicates.
isEmpty implementation depends on how transparency works in your case, if a certain color is considered transparent, below is a case where we have an alpha channel.
threshold is the alpha level that represent the least-visibility for a cell to be considered non-empty.
isBorder will check if any of Moore neighbors is empty, in that case it is a border cell, otherwise it's not because it is surrounded by filled cells.
isEmpty(x,y): image[x,y].alpha <= threshold
isBorder(x,y)
: if isEmpty(x , y-1): return true
: if isEmpty(x , y+1): return true
: if isEmpty(x-1, y ): return true
: if isEmpty(x+1, y ): return true
: if isEmpty(x-1, y-1): return true
: if isEmpty(x-1, y+1): return true
: if isEmpty(x+1, y-1): return true
: if isEmpty(x+1, y+1): return true
: otherwise: return false
getBorderCellList()
: l = empty-list
: for x in 0..image.width
: : for y in 0..image.height
: : : if !isEmpty(x,y)
: : : : if isBorder(x,y)
: : : : : l.add(x,y)
: return l
Optimization You could optimize this by having a pre-computed boolean e[image.width][image.height] where e[x,y] = 1 if image[x,y]is not-empty, then use it directly to check, like isBorder(x,y): e[x-1,y] | e[x+1,y] | .. | e[x+1,y+1].
init()
: for x in 0..image.width
: : for y in 0..image.height
: : : e[x,y] = isEmpty(x,y)
isEmpty(x,y): image[x,y].alpha <= threshold
isBorder(x,y): e[x-1,y] | e[x+1,y] | .. | e[x+1,y+1]
getBorderCellList()
: l = empty-list
: for x in 0..image.width
: : for y in 0..image.height
: : : if not e[x,y]
: : : : if isBorder(x,y)
: : : : : l.add(x,y)
: return l

Resources