I'm trying to randomly instantiate some random sized cubes in a grid but whenever I randomly instantiate said cubes, they tend to overlap. Can someone point me in the right direction to prevent them from overlapping? I tried asking the unity forums and so far, no one has bothered to respond.
Edit: In response, I've added some of the code I'm trying to use. For the most part, I have no clue on how to prevent the cubes from overlapping. This is in C#. Yes I realize I'm not the best coder in the world, that's why I'm stuck on this problem for over 3 weeks.
void InstantiateItems(GameObject[] ItemArray, int Increment)
{
// Select a random treasure from the appropriate treasure array
// TreasureSelect is a public int, ItemArray refers to one of three possible different sized arrays passed into this method
// By calling the random.range and using the passed array's length, you can select a random item from that particular array
TreasureSelect = Random.Range(0, ItemArray.Length);
// Store a random x and y position using info from the GridArray
// Stored Cell is a vector3, GridArray is a 2D array that contains x and y coordinates of a grid's width and height
// The goal is to store the position of a random dirt cube's position from the grid and use that as the position to instantiate a treasure cube item
StoredCell = GridArray[Random.Range(0, GridWidth), Random.Range(0, GridHeight)].transform.position;
// Get the stored x and y offset variables from the treasure cube item's script
// The offset variables are always half of the treasure cube object's scaled x and y values
float incomingX = ItemArray[TreasureSelect].GetComponent<Treasure>().offsetX;
float incomingY = ItemArray[TreasureSelect].GetComponent<Treasure>().offsetY;
// check to see if the object will spawn partially outside of the dirt field
// If the randomly generated x value is greater than the grid's width minus the treasure object script's x value, then adjust the randomly generated x value
if (StoredCell.x >= (float)GridWidth - incomingX)
{
// If determined that it will spawn partially off field, adjust the value
newX = (float)StoredCell.x - incomingX;
// Check to see if the number is odd or not
// If the number is odd, then it will need to have an additional .5 added to make the treasure object line up with the dirt cubes above it (since the pivot point is at the center of the unity cube object)
if(newX % 2 == 1)
{
newX += incomingX;
}
}
else if (StoredCell.x < incomingX)
{
newX = (float)StoredCell.x + incomingX;
if (newX % 2 == 1)
{
newX -= incomingX;
}
}
else
{
newX = StoredCell.x;
if (newX % 2 == 1)
{
newX += incomingX;
}
}
// Now we use the y value instead of the x value and use the grid's height instead of the width
if (StoredCell.y >= (float)GridHeight - incomingY)
{
newY = (float)StoredCell.y - incomingY;
if (newY % 2 == 1)
{
newY += incomingY;
}
}
else if (StoredCell.y < incomingY)
{
newY = (float)StoredCell.y + incomingY;
if (newY % 2 == 1)
{
newY -= incomingY;
}
}
else
{
newY = StoredCell.y;
if (newY % 2 == 1)
{
newY += incomingY;
}
}
// Instantiate the object with a new vector3 using the spawn point object's rotation
Instantiate(ItemArray[TreasureSelect], new Vector3(newX, newY, OffSetZ), ItemArray[TreasureSelect].transform.rotation);
ItemArray[TreasureSelect].transform.position = StoredCell;
}
Related
I'm trying to make a game and I'm stuck on random level design. Basically, I'm trying to create a line from one edge/corner to another edge/corner while having some randomness to it.
See below image 1 [link broken] and 2 for examples. I'm doing this in processing and every attempt I've tried hasn't yielded proper results. I can get them to populate randomly but not in a line or from edge to edge. I'm trying to do this on a 16 x 16 grid by the way. Any ideas or help would be greatly appreciated thanks!
Image 2:
Based on your description, the challenge is in having a connected line from top to bottom with a bit of randomness driving left/right direction.
There are multiple options.
Here's a basic idea that comes to mind:
pick a starting x position: left's say right down the middle
for each row from 0 to 15 (for 16 px level)
pick a random between 3 numbers:
if it's the 1st go left (x decrements)
if it's the 2nd go right (x increments)
if it's the 3rd: ignore: it means the line will go straight down for this iteration
Here's a basic sketch that illustrates this using PImage to visualise the data:
void setup(){
size(160, 160);
noSmooth();
int levelSize = 16;
PImage level = createImage(levelSize, levelSize, RGB);
level.loadPixels();
java.util.Arrays.fill(level.pixels, color(255));
int x = levelSize / 2;
for(int y = 0 ; y < levelSize; y++){
int randomDirection = (int)random(3);
if(randomDirection == 1) x--;
if(randomDirection == 2) x++;
// if randomDirection is 0 ignore as we don't change x -> just go down
// constrain to valid pixel
x = constrain(x, 0, levelSize - 1);
// render dot
level.pixels[x + y * levelSize] = color(0);
}
level.updatePixels();
// render result;
image(level, 0, 0, width, height);
fill(127);
text("click to reset", 10, 15);
}
// hacky reset
void draw(){}
void mousePressed(){
setup();
}
The logic is be pretty plain above, but free to replace random(3) with other options (perhaps throwing dice to determine direction or exploring other psuedo-random number generators (PRNGs) such as randomGaussian(), noise() (and related functions), etc.)
Here's a p5.js version of the above:
let levelSize = 16;
let numBlocks = levelSize * levelSize;
let level = new Array(numBlocks);
function setup() {
createCanvas(320, 320);
level.fill(0);
let x = floor(levelSize / 2);
for(let y = 0 ; y < levelSize; y++){
let randomDirection = floor(random(3));
if(randomDirection === 1) x--;
if(randomDirection === 2) x++;
// if randomDirection is 0 ignore as we don't change x -> just go down
// constrain to valid pixel
x = constrain(x, 0, levelSize - 1);
// render dot
level[x + y * levelSize] = 1;
}
// optional: print to console
// prettyPrintLevel(level, levelSize, numBlocks);
}
function draw() {
background(255);
// visualise
for(let i = 0 ; i < numBlocks; i++){
let x = i % levelSize;
let y = floor(i / levelSize);
fill(level[i] == 1 ? color(0) : color(255));
rect(x * 20, y * 20, 20, 20);
}
}
function prettyPrintLevel(level, levelSize, numBlocks){
for(let i = 0; i < numBlocks; i+= levelSize){
print(level.slice(i, i + levelSize));
}
}
function mousePressed(){
setup();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
The data is a structured a 1D array in both examples, however, if it makes it easier it could easily be a 2D array. At this stage of development, whatever is the simplest, most readable option is the way to go.
I would like to make a simple animation of the character rotating itself when it jumps. I'm making an indie platformer so this should be simple to do, I think, but I'm too newbie for this.
Here's the movement code.
//------------------------- MOVEMENT INPUT
xMove = kRight - kLeft;
xSpd = xMove * mSpd;
ySpd += 0.65;
//------------------------- JUMP
onGround = place_meeting(x,y+1,oSolid);
if(onGround) airJump = 1;
if(kJump){
if(onGround or airJump > 0){
ySpd = -12;
airJump = 0;
}
}
//------------------------- FINAL MOVEMENT
if(place_meeting(x + xSpd, y, oSolid)){
while(!place_meeting(x + sign(xSpd), y, oSolid)) x += sign(xSpd);
xSpd = 0;
}
if(place_meeting(x + xSpd, y + ySpd, oSolid)){
while(!place_meeting(x + xSpd, y + sign(ySpd), oSolid)) y += sign(ySpd);
ySpd = 0;
}
x += xSpd;
y += ySpd;
if xSpd < 0 dir = -1;
if xSpd > 0 dir = 1;
The player is a simple square, so I would like to make it rotate 360 degrees while on the air.
You should be able to use image_angle for this, changing the value will change the angle of the sprite, and continiously increasing/decreasing that value will simulate a rotation.
However, keep in mind that if you rotate the sprite, the hitbox of the sprite will rotate as well. You can probably set the hitbox apart from the sprite so it won't interrupt with each other.
Example:
https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Asset_Management/Sprites/Sprite_Instance_Variables/image_angle.htm
For player movement collision handling you want to avoid using image_angle variable by using your own variable for the image rotation with the draw_sprite_ext function. Also by change you end up wanting to use the image angle for anything its good to wrap it mostly later if your trying to use fov and what not.
For example
function Scr_Player_Create(){
image_offset = 0;
}
function Scr_Player_Step(){
image_offset += (keyboard_check(vk_right) - keyboard_check(vk_left)) * 10;
image_offset = wrap(image_offset, 0, 359);
}
function Scr_Player_Draw(){
draw_sprite_ext( sprite_index, image_index, x, y, image_xscale, image_yscale,
image_angle + image_offset, image_blend, image_alpha );
draw_text(10, 10, image_offset);
}
function wrap(wrap_value, wrap_minimum, wrap_maximum){
// Credit: Juju from GMLscripts forums!
var _mod = ( wrap_value - wrap_minimum ) mod ( wrap_maximum - wrap_minimum );
if ( _mod < 0 ) return _mod + wrap_maximum else return _mod + wrap_minimum;
}
Another approach you could do to avoid image_angle effecting your collision is this
var _angle = image_angle;
image_angle += image_offset;
draw_self();
image_angle = _angle;
Matrix operations performed on the GPU can be pretty hard to debug because GPU operations don't really allow for console logs.
I've written one designed for a real time 2D rendering engine based on a very simple form of I guess what could be called ray casting and am having trouble figuring out what's wrong with it (it's outputting [0,0,0,255,0,0,0,255,...] instead of populating colors).
this.thread.x is the index of the current unit (color channel) in the matrix being operated on.
scene is a buffer made up of 6-unit clumps, each value containing, in order:
The type of entity, always 1 for "sprite" in this case.
The sprite ID, corresponding the the index in this.constants.textures containing the buffer for the entity's sprite.
X offset, the left edge of the sprite
Y offset, the top edge of the sprite
width of the sprite
height of the sprite
bufferWidth is the width of the render area multiplied by 4 channels.
this.constants.textures is an array containing buffers of each sprite which the sprite IDs from the scene refer to.
Note: For those curious, this is being done with GPU.js, a JavaScript lib that converts a JS func into GLSL code to be run via WebGL.
function(scene, sceneLength, bufferWidth) {
var channel = this.thread.x % 4;
if (channel === 3) {
return 255;
}
var x = this.thread.x % bufferWidth;
var y = Math.floor(this.thread.x / bufferWidth);
for (let i1 = 0; i1 < sceneLength; i1 += 6) {
var id = scene[i1 + 1];
var x1 = scene[i1 + 2];
var y1 = scene[i1 + 3];
var w1 = scene[i1 + 4];
var h1 = scene[i1 + 5];
var r1 = scene[i1 + 6];
var offsetX1 = x1 - x;
if (offsetX1 > 0 && offsetX1 < w1) {
var offsetY1 = y1 - y;
if (offsetY1 > 0 && offsetY1 < h1) {
var c1 = offsetY1 * w1 * 4 + offsetX1 * 4;
var c1R = c1 - (c1 % 4);
var c1A = c1R + 3;
if (this.constants.textures[id][c1A] != 0) {
return this.constants.textures[id][c1];
}
}
}
}
return 0;
}
Explanation for the concept I'm trying to implement:
With a matrix operation, when you want to draw a sprite if you were to perform a pass on the entire render area, you'd be doing far more work than necessary. If you break the rendering area down into chunks and only update the sections involved in the sprite being drawn, that would be a fairly decent way to do it. It would certainly be good enough for real time game rendering. This would be a multi-pass approach, where sprites are rendered one at a time.
Alternatively, for what seems to me to be the most optimal approach possible, instead of that, we can use a single-pass approach that performs a single matrix operation for the entire rendering area, evaluating for each color channel what should be there based on doing a very basic form of collision detection with each sprite in the scene and the relevant pixel in that sprite.
You're calculating your sprite offsets backwards, the calculations should be:
var offsetX1 = x - x1;
and
var offsetY1 = y - y1;
The offsets should increase as x and y increase (assuming the sprite co-ordinates have the same co-ordinate system as the screen co-ordinates), so you shouldn't be subtracting x and y.
I have game with map built by rectangles, darker rectangles (named "closed") mean its place where balls should be able to move, ball should reflect from the lighter rectangles(named "open") border. In future I'll add more balls and they will reflect from each other.
The problem is with new Vector after collision.
I force function circleRectGetCollisionNormal() to return vector(-1,0) what i think its normal for this case (ball is moving in right direction).
Ball is starting with degrees and change it simply to vector, this reflection worked for 45 degrees but when I change angle to 10 degrees ball moved into lighter rectangles(named "open").
Here is how it looks like (Picture)
I'm doing like this:
1-check if ball collided with lighter rectangle,
2-if it collided, I want to change direction so I return vector, for example for right side of ball colliding with rectangle return [-1,0] (I think its normal of vertical line, and its pointing left direction).
3-calculate new ball move Vector from this equation: newMoveVector = oldMoveVector − (2 * dotProduct(oldMoveVector, normalVector) * normalVector)
Here is code for each step:
1.
circleRect(circlePos, circleSize, rectPos, rectSize) {
//its rectRect collision but it doesnt matter because reflection surface is always horizontal or vertical
let r1 = {
left: circlePos.x - circleSize.x/2,
right: circlePos.x + circleSize.x/2,
top: circlePos.y - circleSize.y/2,
bottom: circlePos.y + circleSize.y/2
};
let r2 = {
left: rectPos.x,
right: rectPos.x + rectSize.x,
top: rectPos.y,
bottom: rectPos.y + rectSize.y
};
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
isOnOpenTile(pos: Vector, size: Vector) {
let openTiles = this.getTiles('open');
let result = false;
openTiles.forEach(element => {
if( this.circleRect(pos,size,element.pos,element.size) ){
result = element;
return;
}
});
return result;
}
2.
circleRectGetCollisionNormal(c, r) {
if(c.pos.y <= r.pos.y - (r.size.y/2)) return new Vector(0,-1);
//Hit was from below the brick
if(c.pos.y >= r.pos.y + (r.size.y/2)) return new Vector(0,1);
//Hit was from above the brick
if(c.pos.x < r.pos.x) return new Vector(1,0);
//Hit was on left
if(c.pos.x > r.pos.x) return new Vector(-1,0);
//Hit was on right
return false;
}
3.
getNewMoveVector(moveVector, normalVector) {
normalVector = this.normalize(normalVector);
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
return moveVector.sub(dotProduct.mult(normalVector).mult(new Vector(2,2)));
}
normalize(v) {
let length = Math.sqrt((v.x*v.x) + (v.y*v.y));
return new Vector(v.x/length,v.y/length);
}
And here is main function for this
getMoveVectorOnCollision(circle) {
let coll = this.isOnOpenTile( circle.pos, circle.size );
if( coll != false) {
let vector = this.circleRectGetCollisionNormal(circle, coll);
return this.getNewMoveVector(circle.moveVector, vector);
} else return false;
}
Object Vector always contain 2 values all of function (mult, sub, div, add) work like here.
sub(vector: Vector) {
return new Vector(this.x - vector.x, this.y - vector.y);
}
Please give me advice, actual solution or tell about different way to do this reflection. I wasted more than 3 days trying to solve this, I have to move on.
Yor dot product calculation is erroneous. Change these lines:
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
by this one line:
let dotProduct = (moveVector.x * normalVector.x + moveVector.y * normalVector.y);
Note that dotProduct is scalar value, not vector, so you have to make vector for subtraction as
subvec.x = 2 * dotProduct * normalVector.x
subvec.y = 2 * dotProduct * normalVector.y
and
return moveVector.sub(subvec);
I'm having difficulties with the Midpoint Displacement Algorithm using Haxe. I am implementing this by following the steps found here.
First, create an array that represents a blank map. You begin by giving the four corners a random value.
In this square, create the middle point by averaging the four corners and adding a small 'error', or random value. Then create the midpoints of the 4 sides by averaging the two corners each is between. After these steps, you are left with 4 squares. Repeat the steps:
Create the middle point by averaging the four corners and adding a small 'error'.
Create the midpoint of each side by averaging the two corners each point is between.
Each iteration, make the range of the RNG smaller. That way the original few points can have pretty large variation, but the later points only get tiny adjustments. This ensures the right amount of detail in an image.
Here is the function I've written to perform these steps and then normalize the values:
public static function generateFloatMatrix(Columns:Int, Rows:Int, RangeModifier:Float = 0.65):Array<Array<Float>>
{
//Blank 2D Array
var matrix:Array<Array<Float>> = InitFloatMatrix(Columns, Rows);
var range:Float = 1;
//Set Values for all four corners
matrix[0][0] = Math.random() * range;
matrix[Rows-1][0] = Math.random() * range;
matrix[0][Columns-1] = Math.random() * range;
matrix[Rows - 1][Columns - 1] = Math.random() * range;
//Calculates the amount of segments in base 2
var length = Math.sqrt((Columns * Columns) + (Rows * Rows));
var power:Int = Std.int(Math.pow(2, Math.ceil(Math.log(length) / Math.log(2))));
//Stores largest calculated value for normalization
var max:Float = 0;
var width:Int = Std.int(Columns);
var height:Int = Std.int(Rows);
var i:Int = 1;
while (i < power)
{
//Segment Size
width = Std.int(Columns / i);
height = Std.int(Rows / i);
for (y in 0...i)
{
for (x in 0...i)
{
//Top Left Coordinates per segment
var left = width * x;
var top = height * y;
//Find Midpoint
var xMid = Math.ceil(left + (width / 2));
var yMid = Math.ceil(top + (height / 2));
//Make sure right and bottom do not go out of bounds
var right:Int = (left + width < Columns ? left + width : Columns - 1);
var bottom:Int = (top + height < Rows ? top + height : Rows - 1);
//Sets midpoint value to average of all four corners.
matrix[yMid][xMid] =
(matrix[top][left] +
matrix[bottom][left] +
matrix[bottom][right] +
matrix[top][right]) / 4;
//trace ("Top: " + top + " - Left: " + left + " - Bottom: " + bottom + " - Right: " + right);
//Adds random value to midpoint
matrix[yMid][xMid] += Math.random() * range;
//Set side values to average of adjacent corners
matrix[top][xMid] = (matrix[top][left] + matrix[top][right]) / 2;
matrix[bottom][xMid] = (matrix[bottom][left] + matrix[bottom][right]) / 2;
matrix[yMid][left] = (matrix[top][left] + matrix[bottom][left]) / 2;
matrix[yMid][right] = (matrix[top][right] + matrix[bottom][right]) / 2;
max = Math.max(matrix[top][left], max);
}
}
//Reduces range
range *= RangeModifier;
i *= 2;
}
//Normalizes all values in matrix
for (y in 0...Rows)
{
for (x in 0...Columns)
{
matrix[y][x] /= max;
}
}
return matrix;
}
These are the images it is producing if I use each value to render each pixel to the specified coordinate. All the pixels that are rendered white have the value 0, black is value 1.
Your problem is that you don't necessarily hit the already populated pixels with your calculations if your map dimensions are not a power of two. For example if your map is 30 units wide, your grid width is 15 in the first pass and 7 in the second pass, where it bases its calculations on the yet untouched unit 14.
A solution is to do all calculations with floating-point arithmetic until you determine the unit indices, which must of course be integer:
while (i < power)
{
var width:Float = Columns / i; // Floating-point division
var height:Float = Rows / i;
for (y in 0...i)
{
for (x in 0...i)
{
var left:Int = Math.floor(width * x);
var top:Int = Math.floor(height * y);
var xMid:Int = Math.floor(width * (x + 0.5));
var yMid:Int = Math.floor(height * (y + 0.5));
var right:Int = Math.floor(width * (x +1));
var bottom:Int = Math.floor(height * (y + 1));
//Make sure right and bottom do not go out of bounds
if (right > Columns - 1) right = Columns - 1;
if (bottom > Rows - 1) bottom = Rows - 1;
// Do offset and interpolation stuff
}
}
}
This should give you a random map, graph-paper effect and all.
(Caveat: I'm not familiar with Haxe, but have tested this in Javascript, which doesn't have an integer type. I've used Math-floor throughout, where you'll want to do it the Haxe way.)
Finally, it looks to me that you do too many passes. I'd base the power on the maximum of the two dimensions instead of the diagonal. You can also skip the last step where wthe width is near one.