kamiyasi
Member
Hello. I created a script for tile collision checking for my player object, but I have an issue that I haven't been able to figure out a solution for.
Right now, it works great, except that I need to add logic for a collision shape for the player. Currently, my code acts like the player's collision is only a single pixel because I'm using someone's constructor function called TilesetCollisionHandler to handle the tile collision detection. That function looks like this:
Because it's only checking a single pixel, it's not taking the player's collision shape into consideration.
Here's the code that I'm using for the player movement:
I tried adding a lengthdir adjusted value of 32 to the calls for tilesetCollisionHandler.pointOverlappingPixel to try and simulate a collision circle with a radius of 32, but this is not giving the correct results. Instead it just makes the collision jittery and not work properly. Any ideas? Thanks
Right now, it works great, except that I need to add logic for a collision shape for the player. Currently, my code acts like the player's collision is only a single pixel because I'm using someone's constructor function called TilesetCollisionHandler to handle the tile collision detection. That function looks like this:
GML:
function TilesetCollisionHandler(_colliderSprite, _tilemapLayer, _tileSize) constructor {
/* Variables
--------------------------------*/
// Arguments
colliderSprite = _colliderSprite;
tilemapLayer = _tilemapLayer;
tileSize = _tileSize;
// TBD after initialisation
spriteWidth = 0;
spriteHeight = 0;
spritePixelData = {};
/* Initialisation
--------------------------------*/
// Reset sprite pixel data
spritePixelData = {};
// Get sprite info
spriteWidth = sprite_get_width(colliderSprite);
spriteHeight = sprite_get_width(colliderSprite);
// Drawing sprite to surface
var surface = surface_create(spriteWidth, spriteHeight);
surface_set_target(surface);
draw_sprite(colliderSprite, 0, 0, 0);
surface_reset_target();
draw_surface(surface, 0, 0);
// Creating buffer and reading pixel data
var bytes = surface_get_width(surface) * surface_get_height(surface) * 4; // w * h == total pixels and each pixel has 4 bytes
var buffer = buffer_create(bytes,buffer_fast,1);
buffer_get_surface(buffer,surface,0);
// Buffer_get_surface gets pixel data in BGRA format
for (var i = 0; i < bytes; i+=4) {
// Get the 4th byte which relates to alpha. 0 = b, 1 = g, 2 = r, 3 = a
var alpha = buffer_peek(buffer, i+3, buffer_u8);
var pixelIndex = i / 4;
// Convert 1D to 2D array of booleans based on width & height of collision
spritePixelData[pixelIndex mod spriteWidth, floor(pixelIndex / spriteHeight)] = (alpha != 0);
}
// Delete surface & buffer
surface_free(surface);
buffer_delete(buffer);
/* Public purposed functions
--------------------------------*/
/// @function pointOverlappingPixel(_x, _y)
/// @description Returns whether the x, y coordinates passed is a non alpha pixel
/// @param {int} _x The x coordinate to test
/// @param {int} _y The y coordinate to test
pointOverlappingPixel = function(_x, _y)
{
// Get the tile index, and finally interpret to pixel index
var tileIndex = tilemap_get_at_pixel(layer_tilemap_get_id(tilemapLayer), _x, _y);
var tileIndexX = tileIndex mod (spriteWidth / tileSize);
var tileIndexY = floor(tileIndex / (spriteHeight / tileSize));
var tileSpecificX = _x mod tileSize;
var tileSpecificY = _y mod tileSize;
var tilePixelIndexX = (tileIndexX * tileSize) + tileSpecificX;
var tilePixelIndexY = (tileIndexY * tileSize) + tileSpecificY;
// Catch if pixel out of bounds for whatever reason
if(tilePixelIndexX < 0 || tilePixelIndexX >= spriteWidth|| tilePixelIndexY < 0 || tilePixelIndexY >= spriteHeight)
return 0;
return (spritePixelData[tilePixelIndexX, tilePixelIndexY] == 1);
}
}
Here's the code that I'm using for the player movement:
GML:
function playerCollision( mSpd, mDir )
{
var collision = false;
var x_target = x + lengthdir_x( mSpd, mDir );
var y_target = y + lengthdir_y( mSpd, mDir );
if ( tilemap_get_at_pixel( collisionMap, x_target, y_target ) )
{
//collision = true;
collision = tilesetCollisionHandler.pointOverlappingPixel( x_target, y_target )
}
if ( collision == false )
{
x = x_target;
y = y_target;
}
else
{
var angle_precision = 10;
for ( var angle = 1; angle < 90; angle += angle_precision)
{
for ( var multiplier = -1; multiplier <= 1; multiplier += 2) {
// iterate through directions plus and minus 90 degrees for free space to move to
var angle_to_check = mDir + angle * multiplier;
var speed_multiplier = 0.2 + 0.8 *abs( dcos ( angle_difference( angle_to_check, mDir ) ) );
//coordinates to check for collision
x_target = x + lengthdir_x( mSpd * speed_multiplier, angle_to_check );
y_target = y + lengthdir_y( mSpd * speed_multiplier, angle_to_check );
// move if no collision is found
collision = tilesetCollisionHandler.pointOverlappingPixel( x_target, y_target )
if ( collision == false )
{
x = x_target;
y = y_target;
exit;
}
}
}
}
}