I'm working on a small flash game and am currently trying to implement multiplayer for up to 4 players. While the collision detection for 1 player works perfectly, when more players are added only the last player can collide with other objects.
Using trace statements I discovered that calling the x and y coordinates of the problematic players from the main class returns the initial x and y positions and not the current coordinates (trace(players[0].x + "/" + players[0].y);), while calling them from within the player class (trace(this.x + "/" + this.y);) always gives the correct values.
The last player will always return the correct coordinates in both classes. Below is a skeleton of the main class.
public class main extends MovieClip {
public var collisionObject: Array = new Array(14);
public var players: Array = new Array();
private var noOfPlayers = 2;
public function main() {
for (var i = 0; i < noOfPlayers; i++) {
setupPlayer(i);
stage.addChild(players[i]);
}
setupCollisionObject();
stage.addEventListener(Event.ENTER_FRAME, checkForCollision);
}
private function setupCollisionObject() {
/* Determines positions of and spawns objects */
}
private function setupPlayer(playerNo) {
switch (playerNo) {
case 3:
players[3] = new player(1000, 576, 180, 104, 100, 102);
case 2:
players[2] = new player(24, 576, 0, 73, 74, 76);
case 1:
players[1] = new player(1000, 384, 180, 38, 37, 39);
case 0:
players[0] = new player(24, 384, 0, 87, 65, 68);
}
}
public function checkForCollision(e: Event) {
trace("x: "+players[0].x+" y: "+players[0].y);
trace("x: "+players[1].x+" y: "+players[1].y);
for (var i in players) {
for (var j in collisionObject) {
if (players[i].hitTestObject(collisionObject[j])) {
//damage player
}
}
}
}
}
I'm at a loss of why this is happening.
You are missing break; in your switch-case in setupPlayer(), this results in all players but the last reinitialize during each call of setupPlayers(i).
private function setupPlayer(playerNo) {
switch (playerNo) {
case 3:
players[3] = new player(1000, 576, 180, 104, 100, 102);
break;
case 2:
players[2] = new player(24, 576, 0, 73, 74, 76);
break;
case 1:
players[1] = new player(1000, 384, 180, 38, 37, 39);
break;
case 0:
players[0] = new player(24, 384, 0, 87, 65, 68);
break;
}
}
Related
I have a MP3 file in google drive. Is there a way I can get duraton of that MP3 file using google app script?
This page shows how to extract that information from an mp3 file by looking at the byte values.
So I wrote a short example and tested it. It seems to be working fine with all mp3 files I tried so far but there is no guarantee it will always work.
The getPlayTime function returns the play time in seconds.
function getPlayTime(file) {
var bitratesV1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320],
bitratesV2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
var bytes = file.getBlob().getBytes();
for(var pos = 0;pos < bytes.length; pos++) {
if(bytes[pos] === -1 && pos < bytes.length - 3 && (bytes[pos+1]&0xF0) === 0xF0) {
var isMpegVersion2 = (bytes[pos+1]&8) !== 8,
isLayer3 = (bytes[pos+1]&6) === 2,
bitRate = ((bytes[pos+2]&0xF0) >>> 4)&0xF;
if(!isLayer3) continue;
if(isMpegVersion2) bitRate = bitratesV2[bitRate];
else bitRate = bitratesV1[bitRate];
var playTime = bytes.length*8/(1000 * bitRate);
return playTime;
}
}
}
function test() {
var file = DriveApp.getFilesByName("music.mp3").next();
var playTime = getPlayTime(file);
Logger.log(playTime);
}
edit:
Here is a hopefully more accurate but also much slower version
function getRunTime(file) {
var playTime = 0, numFrames = 0;
var bitratesV1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320],
bitratesV2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
var bytes = file.getBlob().getBytes();
for(var pos = 0;pos < bytes.length; pos++) {
if(bytes[pos] === -1 && pos < bytes.length - 3 && (bytes[pos+1]&0xF0) === 0xF0) {
var isMpegVersion2 = (bytes[pos+1]&8) !== 8,
isLayer3 = (bytes[pos+1]&6) === 2,
bitRate = ((bytes[pos+2]&0xF0) >>> 4)&0xF;
if(!isLayer3) continue;
if(isMpegVersion2) bitRate = bitratesV2[bitRate];
else bitRate = bitratesV1[bitRate];
var pt = bytes.length*8/(1000 * bitRate);
if(!isNaN(pt) && isFinite(pt)) {
playTime += pt;
numFrames++;
}
}
}
return playTime/numFrames;
}
Im trying to make a tile array system using flashDevelop and starling.
I think i complete that but i dont understand how i make bounds,intersects between the tile array and my hero.
Note that i make it using tutorials around the internet and not along.
Here are the 2 classes
public class level1 extends Sprite
{
public var map:Array = [
[1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1],
];
//public var object:Image;
//public var data:int;
public function level1()
{
super();
this.addEventListener(starling.events.Event.ADDED_TO_STAGE, onAdd);
}
private function onAdd(event:Event):void
{
drawScreen(map, 90);
}
public function drawScreen(map:Array, cellSize:int = 90):void
{
for(var row:int = 0; row < map.length; row++)
{
for(var column:int = 0; column < map[row].length; column++)
{
var data:int = map[row][column];
// Empty tile, move onto the next item.
if(data == 0) continue;
var object:Image;
if (data == 1) object = new Image(Assets.getTexture("ESAYBTN"));
if (data == 2) object = new Image(Assets.getTexture("TITLE"));
if(object != null)
{
object.x = column * 94;
object.y = row * 29;
stage.addChild(object);
}
}
}
}
}
public class inGame extends Sprite
{
public var Hero:heroClass;
private var enem2:enemy2Class;
private var enemiesArray:Vector.<enemy2Class>;
private var posX:int;
private var posY:int;
private var hitpoints:int;
private var _level1:level1;
public function inGame()
{
super();
this.addEventListener(starling.events.Event.ADDED_TO_STAGE, onAdd);
}
private function onAdd(event:Event):void
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAdd);
enemiesArray = new Vector.<enemy2Class>();
drawScreen();
}
private function drawScreen():void
{
Hero = new heroClass(50, 50, 1);
this.addChild(hero);
_level1 = new level1();
this.addChild(_level1);
createenemies(450, 50, 6);
createenemies(400, 50, 5);
createenemies(350, 50, 4);
createenemies(300, 50, 3);
createenemies(250,50, 2);
}
public function createenemies(posX:int, posY:int, hitpoints:int):void
{
var enemies:enemy2Class = new enemy2Class(posX,posY,hitpoints);
this.addChild(enemies);
enemiesArray.push(enemies);
}
public function hideInGame():void
{
this.visible = false;
}
public function showInGame():void
{
this.visible = true;
this.addEventListener(Event.ENTER_FRAME, gameLoop);
}
private function gameLoop(Event):void
{
var enemiestoloop:enemy2Class;
for (var i:uint = 0; i < enemiesArray.length; i++)
{
enemiestoloop = enemiesArray[i];
//enemiestoloop.x-=2;
if (enemiestoloop.bounds.intersects(enem.bounds))
{
enemiestoloop.x = 400;
enemiestoloop._hitpoints--;
}
if (enemiestoloop._hitpoints <= 0)
{
enemiesArray.splice(i, 1);
this.removeChild(enemiestoloop);
}
}
hero.y++;
if(hero.bounds.intersects("here goes the map???"))
{
hero.y--;
}
}
}
So how i write if the hero hit the map array object 1?
Add all the parts of the tile array to a single display object then just call intersects bounds on the single display object.
I have this code on a game I am making:
override public function update():void
{
var pressed:Boolean = false;
if (collide("ground", x, y))
{
trace("COLLISION");
}
if (Input.check(Key.LEFT))
{
xSpeed -= power;
pressed = true;
}
if (Input.check(Key.RIGHT))
{
xSpeed += power;
pressed = true;
}
if (collide("ground", x, y + 1))
{
onTheGround = true;
ySpeed = 0;
if (Input.check(Key.UP))
{
ySpeed -= jumpPower;
}
} else {
ySpeed += gravity;
}
if (Math.abs(xSpeed) < 1 && !pressed)
{
xSpeed = 0;
}
xSpeed *= hFriction;
ySpeed *= vFriction;
adjustXPosition();
adjustYPosition();
}
And then I have some tiles on the map generated by this class:
public class Level1 extends Entity
{
private var _tiles:Tilemap;
private var _grid:Grid;
public function Level1()
{
_tiles = new Tilemap(Assets.SPRITE_TILESET, 1920, 1080, 120, 120);
graphic = _tiles;
layer = 1;
_tiles.setRect(0, 0, 1920 / 120, 1080 / 120, 1);
_tiles.setRect(0, 17, 1920 / 120, 1, 0);
_grid = new Grid(1920, 1080, 120, 120, 0, 0);
mask = _grid;
_grid.setRect(0, 17, 1920 / 120, 1, true);
type = "ground";
}
}
But when the player touches on the ground, no collision is detected! And the player just falls through it! What is it that is wrong? I thought "type" was gonna make it work, but I guess I was wrong..
if (collide("ground", x, y))
i think you shouldn't use "x,y" instead of "0,0" because this parameters aren't for position of collision, if i remember right, it is for offset of collision as optional.
so, when you use x,y for this parameters, it may be meaning x+x,y+y as position.
so here,
(collide("ground", x, y + 1))
you should use
(collide("ground", 0, 1))
i'm not sure about that, but you can try this.
Did you give your player a proper HitBox? It looks like your Level1 class is set up properly, but you didn't include the code for your player's hitbox.
You can give your player a basic hitbox like so:
player.setHitbox(64, 64, 0, 0);
So I am aware the HTML5 canvas pixel methods are a little different then normal. I am porting a ray-tracer from a Scala Swing application where the old draw method looked like this:
for {
x <- pixels.s.indices
y <- pixels.s(x).indices
} {
g.setColor(pixels.s(x)(y))
g.fillRect(x, y, x, y)
}
Where pixels.s was a 2D array like so:
[[Color, Color, Color],
[Color, Color, Color],
[Color, Color, Color]]
I.e. it mapped exactly to a 2D cartesian plane.
So now I have changed tack slightly, and I have a FLATTENED array of objects with x, y and color:
[{ x: 0, y: 0, color: { red: 33, green: 220, blue: 181 },
{ x: 0, y: 1, color: { red: 33, green: 220, blue: 231 },
{ x: 0, y: 2, color: { red: 33, green: 220, blue: 221 },
...
{ x: 319, y: 238, color: { red: 23, green: 10, blue: 31 },
{ x: 319, y: 239, color: { red: 13, green: 120, blue: 11 }]
I'm trying to draw my flattened array of colors to the screen using this code:
var imageData = context.createImageData(width, height);
var numPixels = width*height;
for (var i = 0; i < numPixels; i++) {
imageData.data[(i*4)] = tracedScene[i].color.red;
imageData.data[(i*4) + 1] = tracedScene[i].color.green;
imageData.data[(i*4) + 2] = tracedScene[i].color.blue;
imageData.data[(i*4) + 3] = 255; // alpha color
};
context.putImageData(imageData, 0, 0);
But it's coming out a bit wrong. I've tripled checked my tests and the actual array is the correct screen data. I'm just rendering it incorrectly. I'm currently getting this:
Whereas I should see:
Now I've had the exact same graphical oddities in the Swing version before (years ago), and it was because of the way I was drawing the image. I can't remember how I fixed it however.
What I am looking to be able to do, is have some method:
drawPixel(x, y, color, canvas) { ... }
that is able to draw a particular pixel at x, y with a color. OR... a way to do it traditionally.
Your input "flattened" array in wrong order: it should be by rows, but yours is by columns.
[{ x: 0, y: 0, color: { red: 33, green: 220, blue: 181 },
{ x: 1, y: 0, color: { red: 33, green: 220, blue: 231 },
{ x: 2, y: 0, color: { red: 33, green: 220, blue: 221 },
...
(Assuming y is top to bottom, x - left to right)
Image data goes from (0, 0) to (1, 0) and your code goes from (0, 0) to (0, 1). In other words, there's a conflict of row/column major order.
Anyway, depending on your performance needs you might be overthinking this.
What I am looking to be able to do, is have some method:
drawPixel(x, y, color, canvas) { ... }
that is able to draw a particular pixel at x, y with a color.
Why not just fill single pixels?
ctx.fillStyle = 'rgba(red, green, blue, alpha)';
ctx.fillRect(x, y, 1, 1);
This will be inefficient at around 100 pixels filled, and you'll want to switch to image data.
If you want to keep your data structure the same you could use something like this, which should work:
var imageData = context.createImageData(width, height);
var numPixels = width*height;
var pixelCount = 0;
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++) {
var wpixel = j * width * 4;
wpixel += i * 4;
console.log(wpixel);
imageData.data[(wpixel)] = tracedScene[pixelCount].red;
imageData.data[(wpixel) + 1] = tracedScene[pixelCount].green;
imageData.data[(wpixel) + 2] = tracedScene[pixelCount].blue;
imageData.data[(wpixel) + 3] = 255; // alpha color
pixelCount++; // independent of i and j the way I wrote it
}
}
context.putImageData(imageData, 0, 0);
Demo: http://jsfiddle.net/t9edv/
My question is, which one is better to use? How to properly use a switch statement. Should I use variables or not etc. I thank you in advance for your reply. "random text because I need a lot of explanation or else it won't let me post."
switch(level_id)
{
case 1:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 2:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 3:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 4:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 5:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 6:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
case 7:
stage.addChild(new lvl(50, 200, 100, level_id));
break;
default:
break;
}
or
switch(level_id)
{
case 1:
x = 50; y = 200; x = 100;
break;
case 2:
x = 50; y = 200; x = 100;
break;
case 3:
x = 50; y = 200; x = 100;
break;
case 4:
x = 50; y = 200; x = 100;
break;
case 5:
x = 50; y = 200; x = 100;
break;
case 6:
x = 50; y = 200; x = 100;
break;
case 7:
x = 50; y = 200; x = 100;
break;
default:
break;
}
stage.addChild(new lvl(x, y, z, level_id));
What I finally did (edit)
Final result, thanks all
var config:Object = {
"1":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"2":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"3":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"4":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"5":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"6":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"7":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 },
"8":{ "paddWidth":50, "blockWidth":200, "blockHeight":100 }
};
stage.addChild(new lvl(
config[level_id].paddWidth,
config[level_id].blockWidth,
config[level_id].blockHeight,
level_id
));
Another option might be (untested code):
var config:Object = {
"1":{ "x":50, "y":100, "z":150 },
"2":{ "x":51, "y":101, "z":151 },
"3":{ "x":52, "y":102, "z":152 },
"4":{ "x":53, "y":103, "z":153 },
"5":{ "x":54, "y":104, "z":154 },
"6":{ "x":55, "y":105, "z":155 },
"7":{ "x":56, "y":106, "z":156 },
"8":{ "x":57, "y":107, "z":157 }
};
x = config[level_id].x;
y = config[level_id].y;
z = config[level_id].z;
stage.addChild(new lvl(x, y, z));
You could also store all the values in some arrays. and access them via index.
var lvlx:Array = [50, 51, 52, 53, 54, 55, 56, 57];
var lvly:Array = [100, 101, 102, 103, 104, 105, 106, 107];
var lvlz:Array = [150, 151, 152, 153, 154, 155, 156, 157];
var lvlIndex:int = level_id - 1;
stage.addChild(new lvl(lvlx[lvlIndex], lvly[lvlIndex], lvlz[lvlIndex]));
You could even make it a two dimensional array, but I thought an array for each x, y, and z was simple, and faster than storing objects in an array.
A really good (and fast) option, would be to use Vectors, with the quick notation:
var lvlx:Vector.<int> = new <int>[50, 51, 52, 53, 54, 55, 56, 57];
There's not much of a difference in your case, but I would go with the latter. There is less code duplication this way (i.e. what happens if you want to change the parent of the level, so instead of stage.addChild you have someContainer.addChild, with the first method you would have to replace every instance of stage with someContainer vs. the second method replacing in only place).
If you're worried about lines of code, you can store the variables on one line:
x = 57; y = 107; z = 157;
As an entirely different approach, I would make a Level class that has the properties x, y and z and the method load.
Then I'd create instances of all the levels that are in the game with the appropriate values and load whichever one is relevant.
Sample:
public class Level
{
public x:int;
public y:int;
public z:int;
public function Level(x:int, y:int, z:int)
{
this.x = x;
this.y = y;
this.z = z;
}
public function load(stage:Stage):void
{
// This is where the Level will do resource consuming stuff, like get
// added to the stage, create level elements, etc.
}
}
Then just create all your levels and store them in an Array:
var levels:Array = [
new Level(50, 100, 120),
new Level(65, 70, 12)
];
Which means it's easy to load whichever level you want now by just having something like this:
function loadLevel(lvl:int):Level
{
var level:Level = levels[lvl];
level.load();
return level;
}