How to implement granular synthesis(pitch shifter)? - javascript

Since music speed and pitch is coupled together, if I speed up music, pitch is also increased. And conversely, if I slow down music, pitch is also decreased.
However, I saw that using granular synthesis, I can decouple speed and pitch. So, I'm currently trying hard to implement granular synthesis.
First of all, I think I succeeded in implementing double speed and half speed, while pitch is same. The code is as same as the following:
※ grain size is 2000. It means that I use 0.04ms of sound as one grain. (2000 samples * 1 s / 44100 samples = 0.04s = 40ms)
// get music
const $fileInput = document.createElement('input');
$fileInput.setAttribute('type', 'file');
document.body.appendChild($fileInput);
$fileInput.addEventListener('change', async (e) => {
const music = await $fileInput.files[0].arrayBuffer();
const actx = new (window.AudioContext || window.webkitAudioContext)({ latencyHint: 'playback', sampleRate: 44100 });
const audioData = await actx.decodeAudioData(music);
const original = audioData.getChannelData(0);
const arr = [];
const grainSize = 2000;
// Please choose one code out of double speed code or half speed code
// copy and paste audio processing code here
});
// double speed
// ex: [0,1,2,3, 4,5,6,7, 8] => [0,1, 4,5, 8] discard 2 items out of 4 items
for (let i = 0; i < original.length; i += grainSize) {
if (original[i + (grainSize / 2) - 1] !== undefined) {
for (let j = 0; j < grainSize / 2; j++) {
arr.push(original[i + j]);
}
} else {
for (let j = i; j < original.length; j++) {
arr.push(j);
}
}
}
// half speed
// ex: [0,1, 2,3, 4] => [0,1,0,0, 2,3,0,0, 4,0,0] add 'two' zeros after every 'two' items
for (let i = 0; i < original.length; i += grainSize) {
if (original[i + grainSize - 1] !== undefined) {
for (let j = 0; j < grainSize; j++) {
arr.push(original[i + j]);
}
} else {
for (let j = i; j < original.length; j++) {
arr.push(original[j]);
}
}
for (let j = 0; j < grainSize; j++) {
arr.push(0);
}
}
// play sound
const f32Arr = Float32Array.from(arr);
const audioBuffer = new AudioBuffer({ length: arr.length, numberOfChannels: 1, sampleRate: actx.sampleRate });
audioBuffer.copyToChannel(f32Arr, 0);
const absn = new AudioBufferSourceNode(actx, { buffer: audioBuffer });
absn.connect(actx.destination);
absn.start();
But the problem is, I totally have no idea how to implement pitch shifter (that is, different pitch, same speed).
As far as I think, same speed means same AudioBuffer size. Therefore, the only variable in my hand is grain size. But I seriously don't know what should I do. It would be greatly appreciated if you share some of your knowledge. Thank you very much!
To Phil Freihofner
Hello, thank you for the kind explanation. I tried your method. As far as I understand, your method is a process that does the following:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // input data (10 samples)
→ [0, 2, 4, 6, 8] // double speed, 1 octave high (sampling interval: 2)
→ [0, 0, 2, 2, 4, 4, 6, 6, 8, 8] // change duration
The result sounds 1 octave high with same duration (successful pitch shifting). However, I don't know what should I do if I do sampling from the input data with sampling interval 1.5? What I mean is I have no idea how to make the length of [0, 1, 3, 4, 6, 7, 9] as the same length with the input data.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // input data
// from
[0, 1, 3, 4, 6, 7, 9] // sampling interval: 1.5
// to
[?, ?, ?, ?, ?, ?, ?, ?, ?, ?]
Meanwhile, I learned that pitch shift can be achieved by a way and as far as I understand, the way is as following:
[1] make each granule be started at the same position as original source
[2] play each granule with different speed.
In addition, I found that I can achieve pitch shifting and time stretching if I transform an input data like the following:
input data = [0, 1, 2, 3, 4, 5, 6, 7]
grain size = 4
<pitch shifting>
in case of p = 2
result = [0, 2, 0, 2, 4, 6, 4, 6] // sounds like one octave high
// If I remember correctly, [0, 2, 0, 0, 4, 6, 0, 0] is also fine
// (and it is more fit to the definition above ([1] and [2])
// but the sound was not good (stuttering).
// I found that [0, 2, 0, 2...] is better.
in case of p = 1.5
result = [0, 1, 3, 0, 4, 5, 7, 4]
in case of p = 0.5
result = [0, 0, 1, 1, 4, 4, 5, 5] // sounds like one octave low
<time stretching>
in case of speed = 2
result = [0, 1, 4, 5]
in case of speed = 1.2
result = [0, 1, 2, 4, 5, 6]
in case of speed = 0.5
result = [0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7]
// If I remember correctly, [0, 1, 2, 3, 0, 0, 0, 0...] is also fine
// but the sound was not good (stuttering)
in case of speed = 0.75
result = [0, 1, 2, 3, 0, 4, 5, 6, 7, 4]
Anyway, thank you for the answer.

I've not read your code close enough to comment on it specifically, but I can comment on the general theory of how pitch shifting is accomplished.
The granules are usually given volume envelopes, with a fade-in and fade-out. I've seen the Hann function (Hanning Window) mentioned as a possibility. Also, the granules are overlapped, with the windowing creating a cross-fade, in effect.
Let's say a granule is 2000 frames, but with the windowing. If you make a granule at every 1000 frames and play them back, overlapping, at the same spacing (every 1000 frames), you should hear the equivalent of the original sound.
Varying the playback distance between the overlapping granules is how the different time lengths of the sound are accomplished. For example, instead of playing a granule every 1000 frames, use 900 or 1100.
I'm pretty sure there are factors to take into consideration concerning the size and shape of the windowing and the range of possible intervals between the granules, but I am not up on them. My simple experiments with this have been with Java and mostly work, but with some artificiality creeping into the playback.
I think consulting at StackOverflow's Signal Processing site would be a good bet for getting more info on the specifics.
EDIT: I just realized that I misread your question! You were asking about changing the pitch while retaining the length of time over which the sounds play. I don't know if this is the "best" way, but I'd consider a plan of doing this in two steps. First, change the sound to the desired pitch. Then, alter the duration of the new sound to be that of the original sound.
The first step can be done with linear interpolation. I tried to explain how to do this in a previous question. For the second step, we break the transformed wave into granules.
However, I just noticed, Spektre has an additional answer on that post that does directly using what you ask, via using FFT. This is probably a better way, but I haven't tried implementing it myself.
EDIT 2, in response to the question added to the OP:
Given, PCM data for 10 frames as follows [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] (I'm using signed floats ranging from -1 to 1. You may have to scale this to convert to your format.)
To change the pitch of the playback to 1.5x (but also changes the length) we get the following data: [0, 0.15, 0.3, 0.45, 0.6, 0.75, 0.9]
The 0.15 is a value that is halfway between points 0.1 and 0.2, arrived at by linear interpolation. If the speed were 1.25x, the data points would be as follows: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, ?? (depends on what follows 0.9)].
The linear intepolation calculation for index 1 in the sequence is as follows:
pitchShiftedAudioData1 = originalPCM1 * (1 - 0.25) + originalPCM2 * 0.75;
In other words, since we land at a point that is 0.25 of the way in between originalPCM1 and originalPCM2, the above calculates what that value would be if the data progressed linearly from 1 to 2.
After doing all this, there would still remain additional steps to form the pitch-shifted data into granules. One has to use a windowing function for each granule. If the window were only 10 frames long (far too short, but will illustrate), a possible window might be the following: [0.01, 0.15 , 0.5 , 0.85 , 1, 1, 0.85, 0.5, 0.15 , 0.01]. (In actuality, it should follow the Hann function.)
This is applied to the data from the different starting points, to create the granules, where N is the index in the array of the signal.
[ signal[N] * window[0], signal[N+1] * window1, signal[N+2] * window2, ..., signal[N+10] * window[10] ]
To create the new signal, the resulting granules are placed sequentially, overlapping, and summed. The relative placement of the granules (how close together or far apart) determines the timing. This is my naive understanding of a brute-force way to accomplish time-shifting, and I've had some OK, not great, results.
I hope this clarifies what I was attempting to describe somewhat!
If you aren't able to follow, please consider unchecking this as the answer. Others may participate that will be able to provide easier to understand information or corrections.
Time-shifting is pretty advanced, IMHO, so expect some complicated calculations and coding (unless someone has a tool to recommend).

Related

JavaScript: Is there an elegant way to generate the coordinates of adjacent cells in a 2d array

let's say I have a grid i.e. 2d array
const grid = [
[0, 0, A, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, B, 0],
[D, E, 0, C, F],
[0, 0, 0, 0, 0],
]
if some cell in the grid can visit all adjacent cells 4-diredtionally, for example, C is at [3, 3] so it can visit [3, 3 + 1], [3 - 1, 3],[3 +1, 3]``[3, 3 - 1], so normally I would have to hard code this like
// 👇 hard-coded directions
const dirs = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
]
const possibleMoves = []
for (const [dx, dy] of dirs) {
possibleMoves.push([dx + x, dy +y])
}
then if it can move 8-directionally then you have to hard code more directions
const dirs = [[1, 0], [-1, 0] , [0,1], [0,-1], [1,1], [-1,1], [-1,-1], [1,-1]]
Is there a smarter way to generate the dirs array for the next moves?
Yes!
First: any time you're doing grid-logic, start by checking what Amit Patel has to say.
Honestly, that link has everything you could ever need.
The short version is: if you know the grid width and cell layout, you can easily calculate coordinate offsets of any cell neighbor for any definition of "neighbor."
That logic can be implemented as a pure function that requires both the grid dimensions and the coordinates of the cell whose neighbors you want (aka the "target" cell):
let myNeighbors = getCellNeighbors(
{ x: 2, y: 2 }, // coords of target cell
{ width: 10, height: 10 } // grid dimensions, in cells
)
Or you can create a stateful thing that takes the grid dimensions at creation and calculates the offsets once, to be re-used for all getNeighbors calls:
let myGrid = new Grid(10, 10)
let myNeighbors = myGrid.getNeighbors(2, 5)
let myBiggerGrid = new Grid(25, 25)
let otherNeighbors = myBiggerGrid(2, 5)

Random vs. Non-Random bullet examples

I got stuck with the following (Phaser/Javascript); There are 4 'bullets' with a timer of 18 seconds. During that 18 seconds, each bullet flys from one side of the game screen (let's say 800 x 600) to the other, i.e. straight across, diagonally, etc.
Got into a debate over random (what this is) vs. non-random. I think non-random would be better in this situation, becuase there is always something not quite right with the random method.
here is the complicated 'random' existing version (the bullet part anyway):
------------------------------------------------->
var bullet = null;
var flagBullet = false;
var flagGameover = false;
var bullet_type = [ 3, 1, 2, 4,];
var init_posX_bullet = [ 0, 0, 0,400,];
var init_posY_bullet = [ 0,600,300, 0,];
var init_idxX_bullet = [-1, -1, -1, 0,];
var init_idxY_bullet = [-1, 1, 0, -1,];
var min_speedX_bullet = [ 5, 5, 5, 0,];
var max_speedX_bullet = [10, 10, 10, 0,];
var min_speedY_bullet = [ 5, -5, 0, 5,];
var max_speedY_bullet = [10,-10, 0, 10,];
var maxTime = 18; //in SECOND
var timerEvent = null;
var canHit = true;
//bullets
bullet = game.add.sprite(-1000,0,"all_bullets");
bullet.anchor.set(0.5);
bullet.type = 1;
bullet.minSpd = 0;
bullet.maxSpd = 0;
bullet.animations.add("b1",[3],1,false);
bullet.animations.add("b2",[0],1,false);
bullet.animations.add("b3",[1],1,false);
bullet.animations.add("b4",[2],1,false);
bullet.animations.play("b1");
function initBullet(){
canHit = true;
<---------------------------------------------------
But these are not your regular game bullets, they are objects; object1, object2, object3, object4 (each one different size/shape - different velocity too) There is a central stationary object in the center of the screen. This is all 2-D.
Pretty 'plain'.
Can not find a good example of this online, there are various tweens, but almost all 'multiple' ones seem to be random.
What would something like this (non-random) look like in code?
start timer (18 seconds)
pause (adjustable)
Object1 flys across the screen (a miss - no collision possible)
pause (adjustable)
Object2 flys across the screen (possible collision)
pause (adjustable)
Object3 flys across the screen (a miss - no collision possible)
pause (adjustable)
Object4 flys across the screen (possible collision)
pause (adjustable)
End Timer (end of game - if no collision)
Anyone else deal with the same or similar situation?

Connect 2 phaser sprites using p2 revolute constraints without apply force

So I have sprites that I want to connect using p2.js' revolute constraints. My current implementation applys force to the sprites as soon as the constraint is created.
How can I avoid this behavior?
If it can't be avoided is there another way to connect 2 sprites with each other horizontally?EDIT:
var Game = {
preload: function() {
game.load.image('tree00', './imgs/tree/tree-00.png');
game.load.image('tree01', './imgs/tree/tree-01.png');
game.load.image('tree02', './imgs/tree/tree-02.png');
game.load.image('tree03', './imgs/tree/tree-03.png');
game.load.image('tree04', './imgs/tree/tree-04.png');
game.load.image('tree05', './imgs/tree/tree-05.png');
game.load.spritesheet('present', './imgs/dude.png', 32, 48);
},
create: function() {
game.physics.startSystem(Phaser.Physics.P2JS);
game.physics.p2.gravity.y = 300;
game.physics.startSystem(Phaser.Physics.ARCADE);
game.stage.backgroundColor = '#aaffee';
treeCollsionGroup = game.physics.p2.createCollisionGroup();
presentCollisionGroup = game.physics.p2.createCollisionGroup();
this.createPresent(game.world.width * 0.21, game.world.height * 0.6);
this.createTree(6, game.world.width * 0.2, game.world.height * 0.8);
//presentCollisionGroup.collides(treeCollsionGroup);
connection[0] = game.physics.p2.createRevoluteConstraint(treeParts[treeParts.length - 1], [(treeParts[treeParts.length - 1].width)/2, treeParts[treeParts.length - 1].height], present, [-present.width/2, treeParts[treeParts.length - 1].height], maxForce);
connection[1] = game.physics.p2.createRevoluteConstraint(treeParts[treeParts.length - 1], [(treeParts[treeParts.length - 1].width)/2, 0], present, [-present.width/2, 0], maxForce);
},
createTree: function(length, xAnchor, yAnchor) {
var lastSprite;
for (var i = 0; i < length; i++) {
newSprite = game.add.sprite(xAnchor, yAnchor - i*100, 'tree0' + i);
newSprite.scale.x = game.world.width/1920;
newSprite.scale.y = game.world.width/1920;
game.physics.p2.enable(newSprite, true);
if (i != length-1) {
newSprite.body.setRectangle(game.world.width * 0.10, newSprite.height * 0.15);
} else {
newSprite.body.setRectangle(newSprite.width * 0.8, newSprite.height * 0.8);
}
newSprite.body.setCollisionGroup(treeCollsionGroup);
if(i === 0) {
newSprite.body.static = true;
}
if (lastSprite) {
switch(i) {
case 1: constraint = game.physics.p2.createRevoluteConstraint(newSprite, [0, 0], lastSprite, [0, -lastSprite.height * 0.62], maxForce);
treeConstraints.push(constraint);
break;
case 2: constraint = game.physics.p2.createRevoluteConstraint(newSprite, [0, 0], lastSprite, [0, -lastSprite.height * 0.285], maxForce);
treeConstraints.push(constraint);
break;
case 3: constraint = game.physics.p2.createRevoluteConstraint(newSprite, [0, 0], lastSprite, [0, -lastSprite.height * 0.425], maxForce);
treeConstraints.push(constraint);
break;
case 4: constraint = game.physics.p2.createRevoluteConstraint(newSprite, [0, 0], lastSprite, [0, -lastSprite.height * 0.4], maxForce);
treeConstraints.push(constraint);
break;
case 5: constraint = game.physics.p2.createRevoluteConstraint(newSprite, [0, 0], lastSprite, [0, -lastSprite.height * 0.55], maxForce);
treeConstraints.push(constraint);
break;
}
}
lastSprite = newSprite;
treeParts.push(newSprite);
newSprite.body.collides(treeCollsionGroup);
}
},
createPresent: function(xAnchor, yAnchor) {
present = game.add.sprite(game.world.width * 0.21, game.world.height * 0.6, 'present');
game.physics.p2.enable(present, true);
present.scale.x = game.world.width/1920;
present.scale.y = game.world.width/1920;
present.body.setRectangle(present.width, present.height);
present.body.data.gravityScale = 0;
present.body.setCollisionGroup(presentCollisionGroup);
}
}
I cut the less important code out so it would not be too much (it already is). Basically what I'm doing is: I create a tree and connecting the parts using revolute constraints so they behave like a tree in the real world (for example in the wind).
Than I create the present which is basically a sprite that should be horizontally connected to the top of the tree. Therefore I use 2 revolute constraint one for the topmost point between the sprites and one for the bottommost. (I know it's kind of dirty code)
After I create these constraints and the present gets connected to the top of the tree the tree starts shaking and collapses (like it should). But I don't want this behaviour.Maybe a lock constraint is what I'm looking for, I have to look into this.
Edit 2:
After taking a look at lock constraints I realized that this is what I'm looking for. But even lock constraints are collapsing the tree.
I can't really understand your issue but have you tried Prismatic or even Lock Constraint? Can give us some code or even codepen example so we may be able to help you better?

Connect Four Game Checking for Wins JS

I am working on my first full program with two weeks of programming under my belt, and have run into a road block I can't seem to figure out. I am making a connect 4 game, and have started by building the logic in JavaScript before pushing to the DOM. I have started to make it with cell objects made by a constructor, that are then pushed into a game object in the form of a 2D array. I have managed to create a function that makes the play each time, and changes the value of the cell at the lowest point of that column with a 2 day array. However, I am not sure how to get my check for wins function to operate.
So far my logic is that, for each point in the 2D array, you can check by row, by column, and by diagonals. I understand the logic of how to check for win, but I don't understand how to traverse through the arrays by row and column. In the example below, this.cellsArray is an array of cell objects in the Board Constructor. The array has 7 column arrays, with 6 rows each, as I flipped the typical row column logic to account for Connect Four's column based nature. However I can't access the array like this.cellsArray[col][row], as col and row aren't defined, and I'm not sure how to define an index value? Any help would be appreciated!
Connect 4
Example:
//array location is equal to an instance of this.cellsArray[col][row]
Board.prototype.checkRowRight = function (arrayLocation) {
if ((arrayLocation[i+1][i].value === arrayLocation.value) && (arrayLocation[i+2][i]=== arrayLocation.value) && (arrayLocation[i+3][i].value === arraylocation.value)){
this.winner = this.currentPlayer;
this.winnerFound = true;
console.log('Winner has been found!')
}
};
Referencing back to my logic found here and refactoring out the winning line detection code, this can easily be converted into Javascript as follows:
function chkLine(a,b,c,d) {
// Check first cell non-zero and all cells match
return ((a != 0) && (a ==b) && (a == c) && (a == d));
}
function chkWinner(bd) {
// Check down
for (r = 0; r < 3; r++)
for (c = 0; c < 7; c++)
if (chkLine(bd[r][c], bd[r+1][c], bd[r+2][c], bd[r+3][c]))
return bd[r][c];
// Check right
for (r = 0; r < 6; r++)
for (c = 0; c < 4; c++)
if (chkLine(bd[r][c], bd[r][c+1], bd[r][c+2], bd[r][c+3]))
return bd[r][c];
// Check down-right
for (r = 0; r < 3; r++)
for (c = 0; c < 4; c++)
if (chkLine(bd[r][c], bd[r+1][c+1], bd[r+2][c+2], bd[r+3][c+3]))
return bd[r][c];
// Check down-left
for (r = 3; r < 6; r++)
for (c = 0; c < 4; c++)
if (chkLine(bd[r][c], bd[r-1][c+1], bd[r-2][c+2], bd[r-3][c+3]))
return bd[r][c];
return 0;
}
And a test call:
x =[ [0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 2, 2, 2, 0],
[0, 1, 2, 2, 1, 2, 0] ];
alert(chkWinner(x));
The chkWinner function will, when called with the board, return the first (and only, assuming each move changes only one cell and you're checking after every move) winning player.
The idea is to basically limit the checks to those that make sense. For example, when checking cells to the right (see the second loop), you only need to check each row 0-6 starting in each of the leftmost four columns 0-3.
That's because starting anywhere else would run off the right hand side of the board before finding a possible win. In other words, column sets {0,1,2,3}, {1,2,3,4}, {2,3,4,5} and {3,4,5,6} would be valid but {4,5,6,7} would not (the seven valid columns are 0-6).
This is an old thread but i'll throw my solution into the mix since this shows up as a top search result for "how to calculate connect4 win javascript"
I tackled this problem by using matrix addition.
Assume your game board is stored in memory as a 2D array like this:
[ [0, 0, 0, 0, 0, 0, 0],
[0, 0, Y, 0, 0, 0, 0],
[0, 0, Y, 0, 0, 0, 0],
[0, 0, R, 0, 0, 0, 0],
[0, 0, Y, 0, 0, 0, 0],
[0, 0, R, R, R, 0, 0] ];
On each "Coin Drop" you should call a function passing the x/y position of the coin.
THIS is where you calculate weather the user has won the game
let directionsMatrix = {
vertical: { south: [1, 0], north: [-1, 0] },
horizontal: { east: [0, 1], west: [0, -1] },
backward: { southEast: [1, 1], northWest: [-1, -1] },
forward: { southWest: [1, -1], northEast: [-1, 1] },
};
NOTE: "South" in matrix notation is [1,0], meaning "Down 1 cell, Right 0 cells"
Now we can loop through each Axis/Direction to check if there is 4 in a row.
const playerHasWon = (colnum, rowNum, playerColor, newGrid) => {
//For each [North/South, East/West, NorthEast/Northwest, SouthEast/Southwest]
for (let axis in directionsMatrix) {
// We difine this variable here so that "East" and "West" share the same count,
// This allows a coin to be dropped in a middle cell
let numMatches = 1;
// For each [North, South]
for (let direction in directionsMatrix[axis]) {
// Get X/Y co-ordinates of our dropped coin
let cellReference = [rowNum, colnum];
// Add co-ordinates of 1 cell in test direction (eg "North")
let testCell = newGrid[cellReference[0]][cellReference[1]];
// Count how many matching color cells are in that direction
while (testCell == playerColor) {
try {
// Add co-ordinates of 1 cell in test direction (eg "North")
cellReference[0] += directionsMatrix[axis][direction][0];
cellReference[1] += directionsMatrix[axis][direction][1];
testCell = newGrid[cellReference[0]][cellReference[1]];
// Test if cell is matching color
if (testCell == playerColor) {
numMatches += 1;
// If our count reaches 4, the player has won the game
if (numMatches >= 4) {
return true;
}
}
} catch (error) {
// Exceptions are to be expected here.
// We wrap this in a try/catch to ignore the array overflow exceptions
// console.error(error);
break;
}
}
// console.log(`direction: ${direction}, numMatches: ${numMatches}`);
// If our count reaches 4, the player has won the game
if (numMatches >= 4) {
return true;
}
}
}
// If we reach this statement: they have NOT won the game
return false;
};
Here's a link to the github repo if you wish to see the full code.
Here's a link to a live demo

Easeljs HitTest and hitRadius

This is my code:
var spriteSheet = new createjs.SpriteSheet({
images: ["images/mario2.png"],
frames: {width:24, height:33, regX: 0, regY: 0},
animations: {
walk_right: [5, 9],
walk_up: [10, 14],
walk_down: [15, 19],
walk_left: [0, 4]
}
});
Player = new createjs.BitmapAnimation(spriteSheet);
Player.name = "Mario";
Player.gotoAndStop("walk_right");
Player.speed = 6;
Player.x = 30;
Player.y = 330;
//Player object toevoegen aan de stage
stage.addChild(Player);
var monsterspritesheet = new createjs.SpriteSheet({
images: ["images/MonsterA.png"], //image to use
frames: {width: 64, height: 54, regX: 0, regY: 0},
animations: {
walk: [0, 9, "walk", 4],
idle: [10, 20, "idle", 4]
}
});
Monster1 = new createjs.BitmapAnimation(monsterspritesheet);
Monster1.name = "Monster";
Monster1.gotoAndStop("walk");
Monster1.speed = 6;
Monster1.x = 180;
Monster1.y = 330;
stage.addChild(Monster1);
stage.update();
For hitTest I use the following code:
var testpos = Player.globalToLocal(Monster1.x ,Monster1.y);
if (Player.hitTest(testpos.x, testpos.y) === true)
{
console.log("HIT");
}
THE PROBLEM:
When my player is below my monster, i get the message "HIT" but I would like to get it to work whenever my player hits the monster and not when it is below it.
I have been playing with regX and regY of both Player & Monster but it doesn't seem to work. Anyone knows the solution?
I think your issue here is that you need a CollisionDetection but hitTest() will only check for one pixel, which is in most cases used for mouse-interaction-detection and is perfect for it. However using it for detecting collisions between two bitmaps might need a little more work than just testing for one pixel.
I would suggest you to use my CollisionDetection class for bitmaps and bitmapAnimations, it works with bounding boxes or pixel-perfect and you basically just need to change one line.
You can check it out on Github: Bitmap Collision Detection for EaselJS
Description is on the github.com Page. (just in case you'll use it: let me know if you run into any bugs)

Categories