Collision Detection Leading to Color Detection

Collision detection leading to color detection?

My suggestion is that your change your "pair" of walls to become a trio of walls instead. This third wall should have a different categoryBitMask. (PhysicsCategory.wallSpace seems to fit with your current naming scheme.)

This "wallSpace" needs to be positioned between the two existing walls. It should be given the same color as its siblings through your color-change logic, but here comes the trick: set it's alpha to 0.

This way you can check for collisions between this invisible wall and your ball and perform actions based on the color-information.

How do I detect a collision with a color?

i do not recomend pixel collision, as it's uncecessary CPU and FPS killer.
here are some collsions that could help you. but if you really need pixel collision, first check rectange or any other collision detection and then if intersect then use pixel collsion to be preciesly... but I don't recomend that, especially in fast games.

here are some collision function that could help you

POLYGON

http://www.codeproject.com/Articles/15573/2D-Polygon-Collision-Detection

CIRCLE:

int circlesColliding(int x1,int y1,int radius1,int x2,int y2,int radius2)
{
//compare the distance to combined radii
int dx = x2 - x1;
int dy = y2 - y1;
int radii = radius1 + radius2;
if ( ( dx * dx ) + ( dy * dy ) < radii * radii )
{
return true;
}
else
{
return false;
}
}

CIRCLE TO RECTANGLE

bool intersects(CircleType circle, RectType rect)
{
circleDistance.x = abs(circle.x - rect.x);
circleDistance.y = abs(circle.y - rect.y);

if (circleDistance.x > (rect.width/2 + circle.r)) { return false; }
if (circleDistance.y > (rect.height/2 + circle.r)) { return false; }

if (circleDistance.x <= (rect.width/2)) { return true; }
if (circleDistance.y <= (rect.height/2)) { return true; }

cornerDistance_sq = (circleDistance.x - rect.width/2)^2 +
(circleDistance.y - rect.height/2)^2;

return (cornerDistance_sq <= (circle.r^2));
}

Processing color collision detection (java)

This was a real head scratcher- good question.

The biggest thing throwing you off is the fact that Processing enables anti-aliasing by default. This causes your colors to be just a little different than what you expect. In most sketches, this is a good thing, since it makes things look better. But since you're using the exact color values, this is screwing you up. You can prove this by taking a screenshot of your sketch and then sampling your colors.

To disable this, simply call noSmooth() at the beginning of your sketch. More info can be found in the reference here.

The next thing screwing you up is that your stroke weight is set to 3, but you're only moving the players 1 pixel at a time. This causes the players to stay "inside" the last point that was drawn, which is why they're constantly running into themselves.

To fix that, you could simply call strokeWeight(1); at the beginning of the draw() function. Or if you need a stroke weight of 3, then make sure you move the player outside of the circle that was just drawn.

That'll fix your problem here, but in the long run, you'd probably be better off keeping track of previous player positions in a data structure like an ArrayList of PVectors. Then you'd redraw them each time draw() was called instead of only drawing them once. And instead of trying to check colors manually, it would be easier to do collision checks in only certain parts of the paths, to avoid the above case.

Color collision detection in Pygame

You can create a Mask from a Color, using pygame.mask.from_threshold, and use the standard pygame colision detection.

Here's an example where I create a Mask using yellow:

import pygame
import random

class Circle(pygame.sprite.Sprite):
def __init__(self, pos, color, *grps):
super().__init__(*grps)
self.image = pygame.Surface((32, 32))
self.image.set_colorkey((1, 2, 3))
self.image.fill((1, 2, 3))
pygame.draw.circle(self.image, pygame.Color(color), (15, 15), 15)
self.rect = self.image.get_rect(center=pos)

def main():
screen = pygame.display.set_mode((800, 600))
colors = ['green', 'yellow', 'white', 'blue']

sprites = pygame.sprite.Group()
objects = pygame.sprite.Group()
for _ in range(20):
pos = random.randint(100, 700), random.randint(100, 600)
Circle(pos, random.choice(colors), sprites, objects)

for sprite in objects:
sprite.mask = pygame.mask.from_threshold(sprite.image, pygame.Color('yellow'), (1, 1, 1, 255))

player = Circle(pygame.mouse.get_pos(), 'dodgerblue', sprites)

while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
player.rect.center = pygame.mouse.get_pos()
if pygame.sprite.spritecollideany(player, objects, pygame.sprite.collide_mask):
screen.fill((255, 255, 255))
else:
screen.fill((30, 30, 30))
sprites.update()
sprites.draw(screen)
pygame.display.flip()
main()

Sample Image

Detecting collision with color on a canvas in HTML5+JavaScript

Well, I found a way to solve this issue :) Hopefully it helps someone...

var box = {
x: 5,
y: 19,
width: 10,
height: 5,
draw: function(ctx){...draw Box on context "ctx"},
touchingColor: function(ctx,r,g,b){
var data = ctx.getImageData(this.x,this.y,this.width,this.height);
for(var i=0;i<data.length;i+=4){
if(
data[i+0]==r&&
data[i+1]==g&&
data[i+2]==b
){
return true;
}
}
return false;
}
};

Detecting when two of the same colors collide

First thing you should do is set up the contactTestBitMasks & categoryBitMasks on all of your SKSpriteNodes, like this -

struct PhysicsCatagory {
static let FirstPerson : UInt32 = 0x1 << 1
static let SecondPerson : UInt32 = 0x1 << 2
}

override func didMoveToView(view: SKView) {
...
firstPerson.SKPhysicsBody?.catagoryBitMask = PhysicsCatagory.FirstPerson
firstPerson.SKPhysicsBody?.contactTestBitMask = PhysicsCatagory.SecondPerson
...
secondPerson.SKPhysicsBody?.catagoryBitMask = PhysicsCatagory.SecondPerson
secondPerson.SKPhysicsBody?.contactTestBitMask = PhysicsCatagory.FirstPerson
...

}

This is just setting up the catagoryBitMask and the contactTestBitMask. The categoryBitMask will be equal to the object you are currently editing, whereas, the contactTestBitMask will be equal to the object you want the object to collide with.

Also, before we move on, we want to add the Contact Delegate to our scene.

class GameScene: SKScene, SKPhysicsContactDelegate{...

And then add the delegate to our scene -

override func didMoveToView(view: SKView) {
...
self.physicsWorld.contactDelegate = self
...

Next, you add the didBeginContact

func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!
}

Lastly inside of that, test...

func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode!
let secondBody = contact.bodyB.node as! SKSpriteNode!

if firstBody.color == secondBody.color{
firstBody.removeFromParent()
secondBody.removeFromParent()
}
}

Hope that helps! :D

How to change color of circle when collision detection occurs?

Semaphores

Use a semaphore that holds the collision state of the circle.

Thus in your Circle.prototype would have something like these functions and properties

Circle.prototype = {
collided: false, // when true change color
draw() {
ctx.strokeStyle = this.collided ? "red" : "blue";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r - 1.5, 0, Math.PI * 2);
ctx.stroke();
},

...
...
// in update
update() {

// when collision is detected set semaphore
if (collision) {
this.collided = true;
}

}
}

Counters

Or you may want to only have the color change last for some time. You can modify the semaphore and use it as a counter. On collision set it to the number of frames to change color for.

Circle.prototype = {
collided: 0,
draw() {
ctx.strokeStyle = this.collided ? (this.collided--, "red") : "blue";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r - 1.5, 0, Math.PI * 2);
ctx.stroke();
},

...
...
// in update
update() {

// when collision is detected set semaphore
if (collision) {
this.collided = 60; // 1 second at 60FPS
}

}
}

Example

This example is taken from another answer I did earlier this year.

As there is a lot of code I have highlighted the relevant code with

/*= ANSWER CODE ==============================================================
...
=============================================================================*/

The example uses counters and changes color for 30 frames after a collision with another ball or wall.

I did not use a semaphore as all the balls would be red within a second.

canvas.width = innerWidth -20;
canvas.height = innerHeight -20;
mathExt(); // creates some additional math functions
const ctx = canvas.getContext("2d");
const GRAVITY = 0;
const WALL_LOSS = 1;
const BALL_COUNT = 10; // approx as will not add ball if space can not be found
const MIN_BALL_SIZE = 6;
const MAX_BALL_SIZE = 30;
const VEL_MIN = 1;
const VEL_MAX = 5;
const MAX_RESOLUTION_CYCLES = 100; // Put too many balls (or too large) in the scene and the
// number of collisions per frame can grow so large that
// it could block the page.

// If the number of resolution steps is above this value
// simulation will break and balls can pass through lines,
// get trapped, or worse. LOL
const SHOW_COLLISION_TIME = 30;
const balls = [];
const lines = [];
function Line(x1,y1,x2,y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype = {
draw() {
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
},
reverse() {
const x = this.x1;
const y = this.y1;
this.x1 = this.x2;
this.y1 = this.y2;
this.x2 = x;
this.y2 = y;
return this;
}
}

function Ball(x, y, vx, vy, r = 45, m = 4 / 3 * Math.PI * (r ** 3)) {
this.r = r;
this.m = m
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;

/*= ANSWER CODE ==============================================================*/
this.collided = 0;
/*============================================================================*/
}
Ball.prototype = {
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += GRAVITY;
},
draw() {

/*= ANSWER CODE ==============================================================*/
ctx.strokeStyle = this.collided ? (this.collided--, "#F00") : "#00F";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r - 1.5, 0, Math.PI * 2);
ctx.stroke();
/* ============================================================================*/

},
interceptLineTime(l, time) {
const u = Math.interceptLineBallTime(this.x, this.y, this.vx, this.vy, l.x1, l.y1, l.x2, l.y2, this.r);
if (u >= time && u <= 1) {
return u;
}
},
checkBallBallTime(t, minTime) {
return t > minTime && t <= 1;
},
interceptBallTime(b, time) {
const x = this.x - b.x;
const y = this.y - b.y;
const d = (x * x + y * y) ** 0.5;
if (d > this.r + b.r) {
const times = Math.circlesInterceptUnitTime(
this.x, this.y,
this.x + this.vx, this.y + this.vy,
b.x, b.y,
b.x + b.vx, b.y + b.vy,
this.r, b.r
);
if (times.length) {
if (times.length === 1) {
if(this.checkBallBallTime(times[0], time)) { return times[0] }
return;
}
if (times[0] <= times[1]) {
if(this.checkBallBallTime(times[0], time)) { return times[0] }
if(this.checkBallBallTime(times[1], time)) { return times[1] }
return
}
if(this.checkBallBallTime(times[1], time)) { return times[1] }
if(this.checkBallBallTime(times[0], time)) { return times[0] }
}
}
},
collideLine(l, time) {

/*= ANSWER CODE ==============================================================*/

this.collided = SHOW_COLLISION_TIME;
/*============================================================================*/
const x1 = l.x2 - l.x1;
const y1 = l.y2 - l.y1;
const d = (x1 * x1 + y1 * y1) ** 0.5;
const nx = x1 / d;
const ny = y1 / d;
const u = (this.vx * nx + this.vy * ny) * 2;
this.x += this.vx * time;
this.y += this.vy * time;
this.vx = (nx * u - this.vx) * WALL_LOSS;
this.vy = (ny * u - this.vy) * WALL_LOSS;
this.x -= this.vx * time;
this.y -= this.vy * time;
},
collide(b, time) { // b is second ball

/*= ANSWER CODE ==============================================================*/

this.collided = SHOW_COLLISION_TIME;
b.collided = SHOW_COLLISION_TIME;
/*============================================================================*/

const a = this;
const m1 = a.m;
const m2 = b.m;
a.x = a.x + a.vx * time;
a.y = a.y + a.vy * time;
b.x = b.x + b.vx * time;
b.y = b.y + b.vy * time;
const x = a.x - b.x
const y = a.y - b.y
const d = (x * x + y * y);
const u1 = (a.vx * x + a.vy * y) / d
const u2 = (x * a.vy - y * a.vx ) / d
const u3 = (b.vx * x + b.vy * y) / d
const u4 = (x * b.vy - y * b.vx ) / d
const mm = m1 + m2;
const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
b.vx = x * vu1 - y * u4;
b.vy = y * vu1 + x * u4;
a.vx = x * vu3 - y * u2;
a.vy = y * vu3 + x * u2;
a.x = a.x - a.vx * time;
a.y = a.y - a.vy * time;
b.x = b.x - b.vx * time;
b.y = b.y - b.vy * time;
},
doesOverlap(ball) {
const x = this.x - ball.x;
const y = this.y - ball.y;
return (this.r + ball.r) > ((x * x + y * y) ** 0.5);
}
}

function canAdd(ball) {
for(const b of balls) {
if (ball.doesOverlap(b)) { return false }
}
return true;
}
function create(bCount) {
lines.push(new Line(-10, 20, ctx.canvas.width + 10, 5));
lines.push((new Line(-10, ctx.canvas.height - 2, ctx.canvas.width + 10, ctx.canvas.height - 30)).reverse());
lines.push((new Line(30, -10, 4, ctx.canvas.height + 10)).reverse());
lines.push(new Line(ctx.canvas.width - 3, -10, ctx.canvas.width - 30, ctx.canvas.height + 10));
while (bCount--) {
let tries = 100;
while (tries--) {
const dir = Math.rand(0, Math.TAU);
const vel = Math.rand(VEL_MIN, VEL_MAX);
const ball = new Ball(
Math.rand(MAX_BALL_SIZE + 30, canvas.width - MAX_BALL_SIZE - 30),
Math.rand(MAX_BALL_SIZE + 30, canvas.height - MAX_BALL_SIZE - 30),
Math.cos(dir) * vel,
Math.sin(dir) * vel,
Math.rand(MIN_BALL_SIZE, MAX_BALL_SIZE),
);
if (canAdd(ball)) {
balls.push(ball);
break;
}
}
}
}
function resolveCollisions() {
var minTime = 0, minObj, minBall, resolving = true, idx = 0, idx1, after = 0, e = 0;
while (resolving && e++ < MAX_RESOLUTION_CYCLES) { // too main ball may create very lone resolution cycle. e limits this
resolving = false;
minObj = undefined;
minBall = undefined;
minTime = 1;
idx = 0;
for (const b of balls) {
idx1 = idx + 1;
while (idx1 < balls.length) {
const b1 = balls[idx1++];
const time = b.interceptBallTime(b1, after);
if (time !== undefined) {
if (time <= minTime) {
minTime = time;
minObj = b1;
minBall = b;
resolving = true;
}
}
}
for (const l of lines) {
const time = b.interceptLineTime(l, after);
if (time !== undefined) {
if (time <= minTime) {
minTime = time;
minObj = l;
minBall = b;
resolving = true;
}
}
}
idx ++;
}
if (resolving) {
if (minObj instanceof Ball) {
minBall.collide(minObj, minTime);
} else {
minBall.collideLine(minObj, minTime);
}
after = minTime;
}
}
}
create(BALL_COUNT);
mainLoop();
function mainLoop() {
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);

resolveCollisions();
for (const b of balls) { b.update() }
for (const b of balls) { b.draw() }

ctx.lineWidth = 1;
ctx.strokeStyle = "#000";
ctx.beginPath();
for(const l of lines) { l.draw() }
ctx.stroke();

requestAnimationFrame(mainLoop);
}

function mathExt() {
Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
Math.randI = (min, max) => Math.random() * (max - min) + min | 0; // only for positive numbers 32bit signed int
Math.randItem = arr => arr[Math.random() * arr.length | 0]; // only for arrays with length < 2 ** 31 - 1
// contact points of two circles radius r1, r2 moving along two lines (a,e)-(b,f) and (c,g)-(d,h) [where (,) is coord (x,y)]
Math.circlesInterceptUnitTime = (a, e, b, f, c, g, d, h, r1, r2) => { // args (x1, y1, x2, y2, x3, y3, x4, y4, r1, r2)
const A = a * a, B = b * b, C = c * c, D = d * d;
const E = e * e, F = f * f, G = g * g, H = h * h;
var R = (r1 + r2) ** 2;
const AA = A + B + C + F + G + H + D + E + b * c + c * b + f * g + g * f + 2 * (a * d - a * b - a * c - b * d - c * d - e * f + e * h - e * g - f * h - g * h);
const BB = 2 * (-A + a * b + 2 * a * c - a * d - c * b - C + c * d - E + e * f + 2 * e * g - e * h - g * f - G + g * h);
const CC = A - 2 * a * c + C + E - 2 * e * g + G - R;
return Math.quadRoots(AA, BB, CC);
}
Math.quadRoots = (a, b, c) => { // find roots for quadratic
if (Math.abs(a) < 1e-6) { return b != 0 ? [-c / b] : [] }
b /= a;
var d = b * b - 4 * (c / a);
if (d > 0) {
d = d ** 0.5;
return [0.5 * (-b + d), 0.5 * (-b - d)]
}
return d === 0 ? [0.5 * -b] : [];
}
Math.interceptLineBallTime = (x, y, vx, vy, x1, y1, x2, y2, r) => {
const xx = x2 - x1;
const yy = y2 - y1;
const d = vx * yy - vy * xx;
if (d > 0) { // only if moving towards the line
const dd = r / (xx * xx + yy * yy) ** 0.5;
const nx = xx * dd;
const ny = yy * dd;
return (xx * (y - (y1 + nx)) - yy * (x -(x1 - ny))) / d;
}
}
}
<canvas id="canvas"></canvas>

can you make it so when two colors collide with each other the game ends?

Well if you just need to check whether any of the rectangles have collided with the player then you can simply add them all to a list and go through that list checking each one for a collision.
Something like this should work:

import pygame
import time

black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)

pygame.init()

size = (610, 410)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Maze Game")
screen.fill(white)
pygame.display.flip()

x = 20
y = 360
x_speed = 0
y_speed = 0

def cube(x, y):
c = pygame.Rect(x + x_speed, y + y_speed, 30, 30)
pygame.draw.rect(screen, red,[x + x_speed, y + y_speed, 30, 30])
return c

rects = []

def rectangles():
#pygame.Rect([0, 0, 610, 410], 10)
#border wasnt working as one of these but works further down#

# add them all to a list
rects.append(pygame.Rect(50, 60, 50, 340))
rects.append(pygame.Rect(100, 60, 50, 50))
rects.append(pygame.Rect(200, 10, 50, 300))
rects.append(pygame.Rect(250, 10, 50, 50))
rects.append(pygame.Rect(150, 150, 50, 200))
rects.append(pygame.Rect(300, 100, 50, 50))
rects.append(pygame.Rect(200, 300, 100, 50))
rects.append(pygame.Rect(350, 60, 50, 340))
rects.append(pygame.Rect(250, 200, 50, 100))
rects.append(pygame.Rect(450, 10, 50, 340))
rects.append(pygame.Rect(550, 60, 50, 340))

def gameover():
font = pygame.font.SysFont(None, 55)
text = font.render("Game Over! Play Again? (y or n)", True, green)
screen.blit(text, [20, 250])
pygame.display.flip()
done = False
while (done == False):
for event in pygame.event.get():
if (event.type == pygame.QUIT):
pygame.quit()
elif(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
pygame.quit()

if(event.type == pygame.KEYDOWN):
if(event.key == pygame.K_y):
board1()
if(event.type == pygame.KEYDOWN):
if (event.key == pygame.K_n):
pygame.quit()

def board1():
x = 15
y = 360
x_speed = 0
y_speed = 0
done = False
while (not done):
for event in pygame.event.get():
if (event.type == pygame.QUIT):
pygame.quit()
elif(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
pygame.quit()

if(event.type == pygame.KEYDOWN):
if(event.key == pygame.K_UP):
y_speed = -.1
if(event.key == pygame.K_DOWN):
y_speed = .1
if(event.key == pygame.K_LEFT):
x_speed = -.1
if(event.key == pygame.K_RIGHT):
x_speed = .1

if(event.type == pygame.KEYUP):
if (event.key == pygame.K_UP):
y_speed = 0
if (event.key == pygame.K_DOWN):
y_speed = 0
if (event.key == pygame.K_LEFT):
x_speed = 0
if (event.key == pygame.K_RIGHT):
x_speed = 0

screen.fill(white)
c = cube(x, y)
r = rectangles()

pygame.draw.rect(screen, black, [0, 0, 610, 410], 10)
pygame.draw.rect(screen, black, [50, 60, 50, 340])
pygame.draw.rect(screen, black, [100, 60, 50, 50])
pygame.draw.rect(screen, black, [200, 10, 50, 300])
pygame.draw.rect(screen, black, [250, 10, 50, 50])
pygame.draw.rect(screen, black, [150, 150, 50, 200])
pygame.draw.rect(screen, black, [300, 100, 50, 50])
pygame.draw.rect(screen, black, [200, 300, 100, 50])
pygame.draw.rect(screen, black, [350, 60, 50, 340])
pygame.draw.rect(screen, black, [250, 200, 50, 100])
pygame.draw.rect(screen, black, [450, 10, 50, 340])
pygame.draw.rect(screen, black, [550, 60, 50, 340])


pygame.display.flip()

x = x + x_speed
y = y + y_speed

# Check for each one of the walls
for rect in rects:
if c.colliderect(rect):
print("collision")
gameover()

board1()

However if you do want to detect by colour, its a bit more complex, but you can take a look at this:
Color collision detection in Pygame.



Related Topics



Leave a reply



Submit