Creating rotation matrix - javascript

I need to create a rotation function that will be used to rotate items around, it nearly works apart from when trying to do -sin.
There doesnt seem to be a function that allows this.
Matrix.createRotation = function (rotation) {
return new Matrix(Math.cos(rotation), Math.sin(rotation), 0,
Math.sin(rotation), Math.cos(rotation), 0, 0, 0, 1);
};

You have to negate the result of Math.sin(rotation) as -Math.sin(rotation):
Matrix.createRotation = function (rotation)
{
return new Matrix(
Math.cos(rotation), -Math.sin(rotation), 0,
Math.sin(rotation), Math.cos(rotation), 0,
0, 0, 1
);
};
Note that -Math.sin(rotation) is faster than (-1)*Math.sin(rotation).

Related

Only Last Player Is Visible In Game

I'm trying to make a simple multiplayer game with HTML, and I can't figure out how to fix the problem with only 1 player being shown. Here's the relevant code:
socket.on('newpos', function(data){
var transform = ctx.getTransform();
var camX, camY;
for(var i = 0 ; i < data.player.length; i++){
ctx.translate(camX, camY);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,10000,10000);
ctx.setTransform(transform);
ctx.drawImage(background, 0, 0, 10000,10000);
ctx.setTransform(1, 0, 0, 1, 0, 0);
if(ID == data.player[i].id){
camX = -data.player[i].x + canvas.width / 2;
camY = -data.player[i].y + canvas.height / 2;
}
ctx.translate(camX,camY);
ctx.drawImage(img, (data.player[i].x),(data.player[i].y),32,32);
});
The code here loops through all the players on the server. It draws the different player's perspectives, but only the last player that joined is visible.
I've tried changing the transforms in different places and everything else I could think of, not sure what to do.
That's because you clear the entire canvas at every iteration, so only the last is visible
Take out/change the position of
ctx.clearRect(0,0,10000,10000)
Also the above code as of now should give an error since it appears the closing bracket of the for loop is missing

Javascript : Phaser call a method inside another method class

I am facing a problem during my development in javascript with Phaser. I try to call a method inside a method of a class but I have an error.
Here is my class :
import Enemy from '../classes/Enemy';
import Turret from '../classes/Turret';
import Bullet from '../classes/Bullet';
class GameScene extends Phaser.Scene {
constructor() {
super({
key: 'GameScene'
});
}
preload()
{
this.load.image('turret', './www/assets/tour_simple.png');
}
drawGrid(graphics)
{
graphics.lineStyle(1, 0x0000ff, 0.8);
for(var i = 0; i < 8; i++)
{
graphics.moveTo(0, i * 64);
graphics.lineTo(640, i * 64);
}
for(var j = 0; j < 10; j++)
{
graphics.moveTo(j * 64, 0);
graphics.lineTo(j * 64, 512);
}
graphics.strokePath();
}
canPlaceTurret(i, j)
{
return this.map[i][j] === 0;
}
placeTurret(pointer)
{
var i = Math.floor(pointer.y/64);
var j = Math.floor(pointer.x/64);
if(this.canPlaceTurret(i, j)) {
var turret = this.turrets.get();
if (turret)
{
turret.setActive(true);
turret.setVisible(true);
turret.place(i, j);
}
}
}
damageEnemy(enemy, bullet)
{
// only if both enemy and bullet are alive
if (enemy.active === true && bullet.active === true) {
// we remove the bullet right away
bullet.setActive(false);
bullet.setVisible(false);
//todo mettre dans une classe constante
var BULLET_DAMAGE = 25;
// decrease the enemy hp with BULLET_DAMAGE
enemy.receiveDamage(BULLET_DAMAGE);
}
}
create() {
this.add.text(200, 230, 'good!', { fill: '#0f0' });
var graphics = this.add.graphics();
this.map = [[ 0,-1, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0,-1, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0,-1,-1,-1,-1,-1,-1,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1, 0, 0]];
//TODO creer une classe qui dessine le path
// the path for our enemies
// parameters are the start x and y of our path
this.path = this.add.path(96, -32);
this.path.lineTo(96, 164);
this.path.lineTo(480, 164);
this.path.lineTo(480, 544);
graphics.lineStyle(3, 0xffffff, 1);
// visualize the path
this.path.draw(graphics);
this.enemies = this.physics.add.group({ classType: Enemy, runChildUpdate: true });
this.nextEnemy = 0;
var graphics = this.add.graphics();
this.drawGrid(graphics);
this.turrets = this.add.group({ classType: Turret, runChildUpdate: true });
this.input.on('pointerdown', this.placeTurret);
this.bullets = this.physics.add.group({ classType: Bullet, runChildUpdate: true });
this.physics.add.overlap(this.enemies, this.bullets, this.damageEnemy);
}
update(time, delta) {
if (time > this.nextEnemy)
{
var enemy = this.enemies.get();
console.log(enemy);
if (enemy)
{
enemy.setActive(true);
enemy.setVisible(true);
// place the enemy at the start of the path
enemy.startOnPath();
this.nextEnemy = time + 2000;
}
}
}
}
export default GameScene;
And an error occurs during my game :
TypeError: this.canPlaceTurret is not a function
The error is from the placeTurret method where I call the canPlaceTurret method.
I have tried some things like adding an attribute to my class : self = this; and call my function with self.canPlaceTurret but there is always the problem. I think it is a scope problem but I don't know how to solve that.
Another important information : we are using Webpack
Thanks for reading :).
Try replacing:
this.input.on('pointerdown', this.placeTurret);
with:
this.input.on('pointerdown', pointer => this.placeTurret(pointer));
(slighthy edited following your comment below)
Basically, when you pass your method as a callback, it becomes a simple function reference. When that function is called later, this won't refer to the class instance you're expecting, because it's no longer called in the context of your class.
By using the fat arrow syntax, you create a specific callback instead, and explicitly tells it to call the method in the context of the current (as in, at the time said callback is defined) this.
Alternatively, you can also do:
this.input.on('pointerdown', this.placeTurret.bind(this));
Instead of creating a new anonymous function, this takes your method as a reference (like it was doing to begin with) but manually binds the current this to it.
Please refer to this post for a more detailed explanation.
Won't it work without using the this keyword at all in your placeTurret() method when calling the canPlaceTurret()?
placeTurret(pointer)
{
var i = Math.floor(pointer.y/64);
var j = Math.floor(pointer.x/64);
if(canPlaceTurret(i, j)) {
var turret = this.turrets.get();
if (turret)
{
turret.setActive(true);
turret.setVisible(true);
turret.place(i, j);
}
}
}

JavaScript context translate doesnt work in electron

I am using ctx.translate(x, y) to move Camera in canvas game. But for some reason, that doesn't work.
This is what I am using:
setCameraPos: function(x, y) {
//ctx.save()
ctx.translate(x, y)
ctx.setTransform(1, 0, 0, 1, 0, 0)
//ctx.restore()
}
It doesn't work at all. It does not change position of camera.
Any errors? No errors at all.
I am using Electron 3.0.3 Beta.
I accept any libraries.
const canvas = document.getElementById('main')
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 30, 30)
// This doesn't work | VVV
ctx.translate(20, 20)
ctx.setTransform(1, 0, 0, 1, 0, 0)
#main {
background-color: black;
}
<canvas id="main">
</canvas>
From what you gave, the translate operation won't work anywhere, not just in Electron.
ctx.setTransform() method sets the transformation matrix to absolute values, the current matrix is discarded and the passed values are the ones to which your matrix will get set.
1, 0, 0, 1, 0, 0 are the values of the native matrix transform (i.e untransformed).
So calling ctx.setTransform(1, 0, 0, 1, 0, 0) will reset your tranform matrix to its default and make all calls to relative translate(), rotate() or transform() useless.
These methods are meant to be relative because they add up to the current matrix values. For instance,
ctx.translate(10, 10);
// here next drawing will be offset by 10px in both x and y direction
ctx.translate(40, -10);
// this adds up to the current 10, 10, so we are now offset by 30, 0
If you want your translate to work, don't call setTransform here, or even replace it with setTransform(1, 0, 0, 1, 20, 20)
Also, in your snippet, you are setting the transformation matrix after you did draw. The transformations will get applied only on next drawings, not on previous ones.
Now, you might be in an animation loop, and need your matrix to get reset at every loop.
In this case, call ctx.setTransform(1,0,0,1,0,0) either at the beginning of your drawing loop, either as the last op, and call translate() before drawing.
const canvas = document.getElementById('main');
const ctx = canvas.getContext('2d');
let x = 0;
ctx.fillStyle = 'red'
anim();
function draw() {
// reset the matrix so we can clear everything
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
//set the transform before drawing
ctx.translate(x - 30, 20)
//which is actually the same as
//ctx.setTransform(1, 0, 0, 1, x, 20);
ctx.fillRect(0, 0, 30, 30);
}
function anim() {
x = (x + 2) % (canvas.width + 60);
draw();
requestAnimationFrame(anim);
}
#main {
background-color: black;
}
<canvas id="main"></canvas>

Limit canvas panning in Fabric JS to a boundary?

I've implemented canvas panning using a Fabric JS canvas, using the below code:
canvas.on("mouse:down", function(e) {
panning = true;
});
canvas.on("mouse:up", function(e) {
panning = false;
});
canvas.on("mouse:move", function(e) {
if (panning) {
var delta = new fabric.Point(e.e.movementX, e.e.movementY);
canvas.relativePan(delta);
}
});
This works fine but you can scroll/pan infinitely in any direction. I want to limit this to a boundary so that a smaller canvas is effectively a view on a larger drawing area. For example a 400 X 400 pixel canvas, which doesn't allow you to pan around more than say 1000 X 1000 pixel area. I've seen in Fabric JS canvas object there's a viewportTransform[] array, which holds zoom level in field [0] and X and Y offsets in fields [4] and [5] but not sure how best to implement a panning boundary. Are there Fabric functions that would make this work?
I also have to take account of zoom level (I'm using canvas.setZoom()) and don't want a user dragging objects beyond the panning boundary either (this may be a separate problem!).
Any thoughts?
Thanks!
there's already a tutorial about it in FabricJS webpage, it works with mousewheel, but you can adapt it: http://fabricjs.com/fabric-intro-part-5
I did it with the some buttons.
First of all I have to tell you that zoom and panning affects canvas viewportTransform property, which is a matrix, similar to css transform property works (or I believe so...).
These are some outputs I got in console while I was working:
originalViewporTransform (6) [1, 0, 0, 1, 0, 0]
zoom
Affects index 0 & 3
zoomViewporTransform (6) [1.2, 0, 0, 1.2, 0, 0]
zoomViewporTransform (6) [1.44, 0, 0, 1.44, 0, 0]
panning
Right:
Affects index 4
panningViewporTransform (6) [1.44, 0, 0, 1.44, -82, 0]
panningViewporTransform (6) [1.44, 0, 0, 1.44, -83, 0]
Left:
Affects index 4
panningViewporTransform (6) [1.728, 0, 0, 1.728, 259, 0]
panningViewporTransform (6) [1.728, 0, 0, 1.728, 260, 0]
Bottom:
Affects index 5
panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, -241]
panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, -242]
Top:
Affects index 5
panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, 305]
panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, 306]
And here's my function to control panning with buttons, in four directions. To limit top and left panning you just have to set the respective viewport to 0 whenever is bigger. When is about right and bottom panning, you have to put the number size of the boundaries you are considering. I was considering the size of my canvas, but you can use which ever size you want.
...
else if (this.actualCanvasViewportTransform[4] < this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom))
...
at those lines you would have to change (this.canvas.getWidth() * this.actualCanvasZoom) with the size you want, like so
(pxLimitRight * this.actualCanvasZoom)
hope it helps
whileMouseDown(caseType){
if (this.actualCanvasZoom <= this.originalCanvasZoom) return;
const units = 1;
let delta;
switch (caseType) {
case 'right':
delta = new fabric.Point(-units,0);
break;
case 'left':
delta = new fabric.Point(units,0);
break;
case 'bottom':
delta = new fabric.Point(0,-units);
break;
case 'top':
delta = new fabric.Point(0,units);
break;
}
this.canvas.relativePan(delta);
// console.log('panningViewporTransform', this.canvas.viewportTransform, this.actualCanvasZoom);
this.actualCanvasViewportTransform = this.canvas.viewportTransform;
/*
WE ARE PANNING LEFT AND RIGHT
*/
if (this.actualCanvasViewportTransform[4] >= 0) {
// WE ARE GOING LEFT
this.actualCanvasViewportTransform[4] = 0;
} else if (this.actualCanvasViewportTransform[4] < this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom))
{
// WE ARE GOING RIGHT
this.actualCanvasViewportTransform[4] = this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom);
}
/*
WE ARE PANNING DOWN AND UP
*/
if (this.actualCanvasViewportTransform[5] >= 0) {
// WE ARE GOING UP
this.actualCanvasViewportTransform[5] = 0;
} else if (this.actualCanvasViewportTransform[5] < this.canvas.getHeight() - (this.canvas.getHeight() * this.actualCanvasZoom)) {
// WE ARE GOING DOWN
this.actualCanvasViewportTransform[5] = this.canvas.getHeight() - (this.canvas.getHeight() * this.actualCanvasZoom);
}
}

How to fill a one-dimensional array grid with various blocks

I have a grid stored in a one-dimensional array. The grid can be of variable size. I now have different blocks (2x2,2x3,2x4,3x3,3x4,3x5, 4x4 and so forth).
How do I fill the grid in now? It can have unused spaces left in the end but what is the fastest way here without looping through the array over and over trying out sizes?
For instance with this 5x5 grid.
var g1 = [
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
]
You could use a backtracking algorithm to solve this type of problem:
function process(var blockstate) {
if(gridIsFullOfBlocks(blockstate))
return true;
if(canPlaceBlock(blockstate)) {
blockstate = placeLargestBlock(blockstate);
return process(blockstate);
}
return false;
}
function fillBlocks(int size) {
var blockstate = new Array(size);
if(process(blockstate))
displayBlockstate(blockstate);
else
displayError(blockstate);
}

Categories

Resources