I am trying to change this paperscript:
<script type="text/paperscript" canvas="canvas-1">
tool.minDistance = 10;
tool.maxDistance = 45;
var path;
function onMouseDown(event) {
path = new Path();
path.fillColor = new Color({ hue: Math.random() * 360, saturation: 1, brightness: 1 });
path.add(event.point);
}
function onMouseDrag(event) {
var step = event.delta / 2;
step.angle += 90;
var top = event.middlePoint + step;
var bottom = event.middlePoint - step;
path.add(top);
path.insert(0, bottom);
path.smooth();
}
function onMouseUp(event) {
path.add(event.point);
path.closed = true;
path.smooth();
}
</script>
to a stand alone javascript like:
paper.install(window);
window.onload = function() {
paper.setup('myCanvas');
tool.minDistance = 10;
tool.maxDistance = 45;
var path;
function onMouseDown(event) {
path = new Path();
path.fillColor = {
hue: Math.random() * 360,
saturation: 1,
brightness: 1
};
path.add(event.point);
}
function onMouseDrag(event) {
var step = event.delta / 2;
step.angle += 90;
var top = event.middlePoint + step;
var bottom = event.middlePoint - step;
path.add(top);
path.insert(0, bottom);
path.smooth();
}
function onMouseUp(event) {
path.add(event.point);
path.closed = true;
path.smooth();
}
}
it give me an error:
TypeError: undefined is not an object (evaluating 'tool.minDistance =
10')
What is tool here? I understand that I might need to declare it before I can use it. Any idea how to resolve this?
You need to make the global scope as outlined in the documentation :
paper.install(window);
Then get on with global defs. :
window.onload = function() {
// Get a reference to the canvas object
paper.setup('myCanvas');
// In your case create tools
var tool = new Tool();
tool.minDistance = 10;
tool.maxDistance = 45;
Then continue as usual, this will set up your tools.. More can be found here.
Incidentally you've actually already done this correctly for Path(), so the same applies to Tool()
When I use Paper.js directly in javascript I prefer to create paper object this way:
var canvas = document.getElementById('canvas-line');
paper.setup(canvas);
// and then if you want to create some Paper.js object prefix it's name with paper
var myPath = new paper.Path();
If you want to use tool you need to decelerate it with new paper.Tool();
For example if you want to check whether path was clicked:
var tool1 = new paper.Tool();
var handle;
var myPath;
myPath.fullySelected = true;
tool1.onMouseDown = function(event) {
handle = null;
// Do a hit test on path for handles:
var hitResult = myPath.hitTest(event.point, {
handles: true,
fill: true,
stroke: true,
segments: true,
tolerance: 2
});
if (hitResult) {
if (hitResult.type == 'handle-in') {
handle = hitResult.segment.handleIn;
} else if (hitResult.type == 'segment') {
handle = hitResult.segment.point;
} else if (hitResult.type == 'handle-out') {
handle = hitResult.segment.handleOut;
}
}
}
You can find more informations about tools in here http://paperjs.org/reference/tool/
Related
I have this paperscript from paper.js and i would like it to be converted in javascript but i cant get the mouse drag to work.
PaperScript recognises a couple of special event handlers when they are declared as global functions, while in JavaScript, these need to be manually installed on the appropriate object.
Codepen of the paperscript
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-full.js"></script>
<!-- Define inlined PaperScript associate it with myCanvas -->
<script type="text/paperscript" canvas="myCanvas">
var rectangle = new Rectangle(new Point(50, 50), new Point(150, 100));
var path = new Path.Rectangle(rectangle);
path.fillColor = '#e9e9ff';
path.strokeColor = 'black';
path.strokeWidth = 2;
path.selected = true;
path.closed = true;
var hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 8
};
var segment, path;
var movePath = false;
function onMouseDown(event) {
segment = path = null;
var hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult)
return;
if (event.modifiers.shift) {
if (hitResult.type == 'segment') {
hitResult.segment.remove();
};
return;
}
if (hitResult) {
path = hitResult.item;
if (hitResult.type == 'segment') {
segment = hitResult.segment;
} else if (hitResult.type == 'stroke') {
var location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
//path.smooth();
}
}
movePath = hitResult.type == 'fill';
if (movePath)
project.activeLayer.addChild(hitResult.item);
}
function onMouseMove(event) {
project.activeLayer.selected = false;
if (event.item)
event.item.selected = true;
}
function onMouseDrag(event) {
if (segment) {
segment.point += event.delta;
//path.smooth();
} else if (path) {
path.position += event.delta;
}
}
</script>
<canvas id="myCanvas" resize></canvas>
Thank you in advance!
Differences between PaperScript and JavaScript contexts are detailled here.
In order to make the less changes possible to your code, you have to:
Install Paper.js in global scope. This allow you to use classes like Path, Point, ... directly (without passing trough global paper object).
Setup Paper.js to use your canvas. This is equivalent to setting the PaperScript canvas attribute.
Create a Tool instance that you will use to register your event handlers.
Use math operator functions (like Point.add()) instead of operators (like +) when manipulating points.
Here is your code working in JavaScript context.
// expose paperjs classes into global scope
paper.install(window);
// bind paper to the canvas
paper.setup('canvas');
var rectangle = new Rectangle(new Point(50, 50), new Point(150, 100));
var path = new Path.Rectangle(rectangle);
path.fillColor = '#e9e9ff';
path.strokeColor = 'black';
path.strokeWidth = 2;
path.selected = true;
path.closed = true;
var hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 8
};
var segment, path;
var movePath = false;
// create a custom tool
var customTool = new Tool();
// attach handlers to the tool
customTool.onMouseDown = function(event) {
segment = path = null;
var hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult) {
return;
}
if (event.modifiers.shift) {
if (hitResult.type == 'segment') {
hitResult.segment.remove();
}
return;
}
if (hitResult) {
path = hitResult.item;
if (hitResult.type == 'segment') {
segment = hitResult.segment;
} else if (hitResult.type == 'stroke') {
var location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
//path.smooth();
}
}
movePath = hitResult.type == 'fill';
if (movePath) {
project.activeLayer.addChild(hitResult.item);
}
};
customTool.onMouseMove = function(event) {
project.activeLayer.selected = false;
if (event.item) {
event.item.selected = true;
}
};
customTool.onMouseDrag = function(event) {
if (segment) {
// use methods instead of operators
segment.point = segment.point.add(event.delta);
//path.smooth();
} else if (path) {
path.position = path.position.add(event.delta);
}
};
html,
body {
margin: 0;
overflow: hidden;
height: 100%;
}
canvas[resize] {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.min.js"></script>
<canvas id="canvas" resize></canvas>
I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object
$(document).ready(function () {
Crafty.init(window.innerWidth, window.innerHeight);
var player = new controller(37,38,39,40);
player.d.color("red").attr({
w: 50,
h: 50,
x: 0,
y: 0
});
// Jump Height = velocity ^ 2 / gravity * 2
// Terminal Velocity = push * (1 / viscosity)
var gravity = 1;
var viscosity = 0.5;
var frame = (1 / 20);
var distanceMultiplier = 10; //pixels per meter
var timeMultiplier = 20; //relative to actual time
var keystart = [];
var keyboard = [];
function controller (controls) {
this.d = Crafty.e();
this.d.addComponent("2D, Canvas, Color, Collision");
this.d.collision();
this.d.mass = 1;
this.d.a = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0,
resistance : 0
};
this.d.a.push = 0;
this.d.v = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0
};
this.d.jumping = true;
this.d.onHit("Collision", function () {
var a = this.d.hit("Collision");
if (a) {
for (var b in a) {
this.d.x = this.d.x - a[b].normal.x * a[b].overlap;
this.d.y = this.d.y - a[b].normal.y * a[b].overlap;
if (a[b].normal.y < -0.5) {
this.d.jumping = false;
}
if (Math.abs(a[b].normal.x) < 0.2) {
this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2;
}
if (Math.abs(a[b].normal.y) < 0.2) {
this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2;
}
}
return;
}
});
this.d.physics = function () {
if (keyboard[arguments[1]] && !this.jumping) {
this.v.velocity = 5;
this.jumping = true;
}
if (keyboard[arguments[1]] && this.jumping) {
var now = new Date();
if (now.getTime() - keystart[arguments[1]].getTime() < 500) {
this.v.velocity = 5;
}
}
if (keyboard[arguments[0]] && keyboard[arguments[2]]) {
this.a.velocity = 0;
} else {
if (keyboard[arguments[0]]) {
this.a.velocity = -3;
}
if (keyboard[arguments[2]]) {
this.a.velocity = 3;
}
}
if (keyboard[arguments[3]]) {
this.v.velocity = -5;
}
this.a.force = this.a.push - this.a.resistance;
this.a.acceleration = this.a.force / this.mass;
this.a.velocity = this.a.velocity + (this.a.acceleration * frame);
this.a.extradistance = (this.a.velocity * frame);
this.a.resistance = this.a.velocity * viscosity;
this.attr({
x: (this.x + (this.a.extradistance * distanceMultiplier))
});
this.v.force = gravity * this.mass;
this.v.acceleration = this.v.force / this.mass;
this.v.velocity = this.v.velocity - (this.v.acceleration * frame);
this.v.extradistance = (this.v.velocity * frame);
this.attr({
y: (this.y - (this.v.extradistance * distanceMultiplier))
});
setTimeout(this.physics, (frame * 1000) / timeMultiplier);
};
this.d.listen = function(){ document.body.addEventListener("keydown", function (code) {
var then = new Date();
if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
});
};
}
player.d.physics();
player.d.listen();
document.body.addEventListener("keyup", function (code) {
keyboard[code.keyCode] = false;
});
});
In trying to put the functions as prototypes of the class, I run into a problem.
Crafty.init(500,500);
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
If an object is defined in the constructor, I cannot use it to add a prototype to.
Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init.
I haven't looked through all of your example code, because there's a lot! But consider the second block:
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this:
// Define a new component with Crafty.c(), rather than creating a class
Crafty.c("Block", {
// On init, add the correct components and setup the color and dimensions
init: function() {
this.requires("2D, Color, Canvas")
.color("red")
.attr({x:0,y:0,h:50,w:50});
},
// method for changing color
green: function() {
this.color("green");
}
});
// Create an entity with Crafty.e()
block1 = Crafty.e("Block");
// It's not easy being green!
block1.green();
I have a canvas animation that has been created with createjs. The entire animation script including init() function is loaded via jquery: $.getScript() on page load.
The init() and handlecomplete() function included below is then run which attaches the animation to a html canvas element on the page.
var canvas, stage, exportRoot, audio;
var tweens = [];
function init() {
canvas = document.getElementById("canvas");
images = images||{};
if (stage) {
stage.enableDOMEvents(false);
stage.removeAllChildren();
createjs.Ticker.removeAllEventListeners()
stage.enableDOMEvents(true);
}
if (audio ) {
audio.stop();
}
removeTweens();
exportRoot = null;
audio = null;
stage = null;
var loader = new createjs.LoadQueue(false);
loader.installPlugin(createjs.Sound);
loader.addEventListener("fileload", handleFileLoad);
loader.addEventListener("complete", handleComplete);
loader.loadManifest(lib.properties.manifest);
}
function handleComplete() {
exportRoot = new lib.animation2();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
stage.canvas.width = 1280;
stage.canvas.height = 720;
resizeToFit();
stage.update();
createjs.Ticker.setFPS(lib.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
createjs.Ticker.addEventListener("tick", updateTimer);
if (lib.properties.audiovolume) {
audio = createjs.Sound.play("audio", createjs.Sound.INTERRUPT_EARLY, 0, 0, -1, lib.properties.audiovolume);
}
exportRoot.gotoAndPlay(startFrame );
}
My issue is when the user makes a change, we load the script a second time using the same jquery method which returns the updated script. The init() function then executes properly and the new animation plays correctly, but our animated text (using the animateText below) does not appear on the canvas. This function is also loaded dynamically with other functions.
Checking the tween arrays, they are being created and removed as required, but they are not visible.
They are either layered behind the new animation, or not being attached to the new canvas or something else?
Simply refreshing the page will then load the new script and text properly. So clearly something in the dynamic loading of the script?
var animateText = function(localString, startX, startY, letterClip, endObject, font, color) {
var waitAmount = 0;
var offSetAmount = 20;
for(var i = 0; i < localString.length; i++){
var fl_MyInstance = new letterClip();
fl_MyInstance.localName.text = localString[i];
if(font != null){
fl_MyInstance.localName.font = font;
}
if(color != null){
fl_MyInstance.localName.color = color;
}
var localX = startX;
var localY = startY;
fl_MyInstance.x = startX + offSetAmount;
var beginX = startX + offSetAmount
offSetAmount = offSetAmount - 4
fl_MyInstance.y = startY;
fl_MyInstance.alpha = 0;
fl_MyInstance.scaleX = 0.1;
fl_MyInstance.scaleY = 0.1;
var bounds = fl_MyInstance.getBounds();
startX += bounds.width + 0;
var target = fl_MyInstance;
var tween = createjs.Tween.get(target, {
loop: false
}).wait(waitAmount)
.to({
x: localX,
y: localY,
alpha: 1,
scaleX: 1,
scaleY: 1
}, 400, createjs.Ease.circOut);
tween.waitAmount = waitAmount;
if(endObject == null){
tween.endObject = {
x: localX,
y: localY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}
} else {
tween.endObject = {
x: localX - endObject.x,
y: localY - endObject.y,
alpha: endObject.alpha,
scaleX: endObject.scaleX,
scaleY: endObject.scaleY
}
}
tween.targetClip = fl_MyInstance;
tween.arrayIndex = tweens.length;
tweens.push(tween);
waitAmount += 20;
stage.addChild(fl_MyInstance);
}
}
var removeTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
stage.removeChild(tween.targetClip);
tweens[tween.arrayIndex] = null;
}
}
}
var closeTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
createjs.Tween.get(tween.targetClip, {
loop: false
}).wait(tween.waitAmount).to(tween.endObject, 400, createjs.Ease.circOut).call(function(){
stage.removeChild(tween.targetClip);
tweens[tween.arrayIndex] = null;
});
}
}
}
var finalTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
createjs.Tween.get(tween.targetClip, {
loop: false
}).to(tween.endObject, 400, createjs.Ease.circOut);
}
}
}
Since the rest of the animation works perfectly using this method of dynamic loading, I don't think it is something in the loading. But there must be something missing in animateText and reloading functions that causes the issue.
I am reading and trying to do this tutorial:
http://www.sitepoint.com/creating-a-simple-windows-8-game-with-javascript-input-and-sound/
Yesterday, I wrote in this forum with one error, and solved it, but today, I'm in the final one and getting another error.
My default.html file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>CatapultGame</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
<!-- CatapultGame references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
<script src="js/CreateJS/easeljs-0.6.0.min.js"></script>
<script src="js/CreateJS/preloadjs-0.2.0.min.js"></script>
</head>
<body>
<canvas id="gameCanvas"></canvas>
</body>
</html>
and default.js file :
// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
WinJS.strictProcessing();
var canvas, context, stage;
var bgImage, p1Image, p2Image, ammoImage, p1Lives, p2Lives, title, endGameImage;
var bgBitmap, p1Bitmap, p2Bitmap, ammoBitmap;
var preload;
// calculate display scale factor - original game assets assume 800x480
var SCALE_X = window.innerWidth / 800;
var SCALE_Y = window.innerHeight / 480;
var MARGIN = 25;
var GROUND_Y = 390 * SCALE_Y;
var LIVES_PER_PLAYER = 3;
var player1Lives = LIVES_PER_PLAYER;
var player2Lives = LIVES_PER_PLAYER;
var isShotFlying = false;
var playerTurn = 1;
var playerFire = false;
var shotVelocity;
var MAX_SHOT_POWER = 10;
var GRAVITY = 0.07;
var isAiming = false;
var aimPower = 1;
var aimStart, aimVector;
var FIRE_SOUND_FILE = "/sounds/CatapultFire.wav";
var HIT_SOUND_FILE = "/sounds/BoulderHit.wav";
var EXPLODE_SOUND_FILE = "/sounds/CatapultExplosion.wav";
var LOSE_SOUND_FILE = "/sounds/Lose.wav";
var AIM_SOUND_FILE = "/sounds/RopeStretch.wav";
var WIN_SOUND_FILE = "/sounds/Win.wav";
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
args.setPromise(WinJS.UI.processAll());
}
};
function initialize() {
canvas = document.getElementById("gameCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context = canvas.getContext("2d");
canvas.addEventListener("MSPointerUp", endAim, false);
canvas.addEventListener("MSPointerMove", adjustAim, false);
canvas.addEventListener("MSPointerDown", beginAim, false)
**var stage = new createjs.Stage(canvas);** <<========== HERE IS THE ERROR LINE !!!!
// use preloadJS to get sounds and images loaded before starting
preload = new createjs.PreloadJS();
preload.onComplete = prepareGame;
var manifest = [
{ id: "screenImage", src: "images/Backgrounds/gameplay_screen.png" },
{ id: "redImage", src: "images/Catapults/Red/redIdle/redIdle.png" },
{ id: "blueImage", src: "images/Catapults/Blue/blueIdle/blueIdle.png" },
{ id: "ammoImage", src: "images/Ammo/rock_ammo.png" },
{ id: "winImage", src: "images/Backgrounds/victory.png" },
{ id: "loseImage", src: "images/Backgrounds/defeat.png" },
{ id: "blueFire", src: "images/Catapults/Blue/blueFire/blueCatapultFire.png" },
{ id: "redFire", src: "images/Catapults/Red/redFire/redCatapultFire.png" },
{ id: "hitSound", src: HIT_SOUND_FILE },
{ id: "explodeSound", src: EXPLODE_SOUND_FILE },
{ id: "fireSound", src: FIRE_SOUND_FILE },
{ id: "loseSound", src: LOSE_SOUND_FILE },
{ id: "aimSound", src: AIM_SOUND_FILE },
{ id: "winSound", src: WIN_SOUND_FILE }
];
preload.loadManifest(manifest);
}
function prepareGame()
{
// draw Bg first, others appear on top
bgImage = preload.getResult("screenImage").result;
bgBitmap = new createjs.Bitmap(bgImage);
bgBitmap.scaleX = SCALE_X;
bgBitmap.scaleY = SCALE_Y;
stage.addChild(bgBitmap);
// draw p1 catapult
p1Image = preload.getResult("redImage").result;
p1Bitmap = new createjs.Bitmap(p1Image);
p1Bitmap.scaleX = SCALE_X;
p1Bitmap.scaleY = SCALE_Y;
p1Bitmap.x = MARGIN;
p1Bitmap.y = GROUND_Y - p1Image.height * SCALE_Y;
stage.addChild(p1Bitmap);
// draw p2 catapult and flip
p2Image = preload.getResult("blueImage").result;
p2Bitmap = new createjs.Bitmap(p2Image);
p2Bitmap.regX = p2Image.width;
p2Bitmap.scaleX = -SCALE_X; // flip from right edge
p2Bitmap.scaleY = SCALE_Y;
p2Bitmap.x = canvas.width - MARGIN - (p2Image.width * SCALE_X);
p2Bitmap.y = GROUND_Y - (p2Image.height * SCALE_Y);
stage.addChild(p2Bitmap);
// draw the boulder, and hide for the moment
ammoImage = preload.getResult("ammoImage").result;
ammoBitmap = new createjs.Bitmap(ammoImage);
ammoBitmap.scaleX = SCALE_X;
ammoBitmap.scaleY = SCALE_Y;
ammoBitmap.visible = false; // hide until fired
stage.addChild(ammoBitmap);
// player 1 lives
p1Lives = new createjs.Text("Lives Left : " + player1Lives, "20px sans-serif", "red");
p1Lives.scaleX = SCALE_X;
p1Lives.scaleY = SCALE_Y;
p1Lives.x = MARGIN;
p1Lives.y = MARGIN * SCALE_Y;
stage.addChild(p1Lives);
//player 2 lives
p2Lives = new createjs.Text("Lives Left : " + player2Lives, "20px sans-serif", "red");
p2Lives.scaleX = SCALE_X;
p2Lives.scaleY = SCALE_Y;
p2Lives.x = canvas.width - p2Lives.getMeasuredWidth() * SCALE_X - MARGIN;
p2Lives.y = MARGIN * SCALE_Y;
stage.addChild(p2Lives);
// game title
title = new createjs.Text("Catapult Wars", "30px sans-serif", "black");
title.scaleX = SCALE_X;
title.scaleY = SCALE_Y;
title.x = canvas.width / 2 - (title.getMeasuredWidth() * SCALE_X) / 2
title.y = 30 * SCALE_Y;
stage.addChild(title);
stage.update();
startGame();
}
function startGame()
{
Ticker.setInterval(window.requestAnimationFrame);
Ticker.addListener(gameLoop);
}
function gameLoop()
{
update();
draw();
}
function update() {
if (isShotFlying)
{
// shot in the air
ammoBitmap.x += shotVelocity.x;
ammoBitmap.y += shotVelocity.y;
shotVelocity.y += GRAVITY; //apply gravity to the y(height) values only, obviously
if (ammoBitmap.y + ammoBitmap.image.height >= GROUND_Y ||
ammoBitmap.x <= 0 ||
ammoBitmap.x + ammoBitmap.image.width >= canvas.width)
{
// missed
isShotFlying = false; //stop shot
ammoBitmap.visible = false;
playerTurn = playerTurn % 2 + 1; // invert player ( switch between 1 and 2)
}
else if (playerTurn == 1)
{
if (checkHit(p2Bitmap)) {
// Hit
p2Lives.text = "Lives Left : " + --player2Lives;
processHit();
}
}
else if (playerTurn == 2)
{
if (checkHit(p1Bitmap))
{
// Hit
p1Lives.text = "Lives Left : " + --player1Lives;
processHit();
}
}
}
// No current shot, should either player fire ?
else if (playerTurn == 1)
{
// does the player want to fire ?
if (playerFire)
{
playerFire = false;
ammoBitmap.x = p1Bitmap.x + (p1Bitmap.image.width * SCALE_X / 2);
ammoBitmap.y = p1Bitmap.y;
shotVelocity = aimVector;
fireShot();
}
}
else if (playerTurn == 2)
{
// AI automatically fires (randomly on it's turn)
ammoBitmap.x = p2Bitmap.x + (p2Bitmap.image.width * SCALE_X / 2);
ammoBitmap.y = p2Bitmap.y;
shotVelocity = new createjs.Point(
Math.random() * (-4 * SCALE_X) - 3,
Math.random() * (-3 * SCALE_Y) - 1);
fireShot();
}
}
// triggered by MSPointerDown event
function beginAim(event)
{
if (playerTurn == 1)
{
if (!isAiming)
{
aimStart = new createjs.Point(event.x, event.y);
isAiming = true;
}
}
}
// triggered by MSPointerMove event
function adjustAim(event)
{
if (isAiming)
{
var aimCurrent = new createjs.Point(event.x, event.y);
aimVector = calculateAim(aimStart, aimCurrent);
// TODO write text and/or show aiming arrow on screen
Debug.writeln("Aiming..." + aimVector.x + "/" + aimVector.y);
}
}
// triggered by MSPointerUp event
function endAim(event)
{
if (isAiming) {
isAiming = false;
var aimCurrent = new createjs.Point(event.x, event.y);
aimVector = calculateAim(aimStart, aimCurrent);
playerFire = true;
}
}
function calculateAim(start, end)
{
// this only works for player 1
var aim = new createjs.Point(
(end.x - start.x) / 80,
(end.y - start.y) / 80);
aim.x = Math.min(MAX_SHOT_POWER, aim.x); // cap velocity
aim.x = Math.max(0, aim.x); // fire forward only
aim.y = Math.max(-MAX_SHOT_POWER, aim.y);/// cap velocity
aim.y = Math.min(0, aim.y); // fire up only
return aim;
}
function checkHit(target)
{
// EaselJS hit test doesn't factor in scaling
// so use simple bounding box vs center of rock
// get centre of rock
var shotX = ammoBitmap.x + ammoBitmap.image.width / 2;
var shotY = ammoBitmap.y + ammoBitmap.image.height / 2;
// return wether center of rock is in rectangle bounding target player
return (((shotX > target.x) &&
(shotX <= target.x + (target.image.width * SCALE_X)))
&&
((shotY >= target.y) &&
(shotY <= target.y + (target.image.height * SCALE_Y))));
}
function fireShot()
{
playSound(FIRE_SOUND_FILE);
ammoBitmap.visible = true;
isShotFlying = true;
}
function processHit()
{
playSound(EXPLODE_SOUND_FILE);
isShotFlying = false; // stop shot
ammoBitmap.visible = false; // hide shot
playerTurn = playerTurn % 2 + 1; // change player
if ((player1Lives <= 0) || (player2Lives <= 0)) {
endGame();
}
}
function endGame()
{
Ticker.setPaused(true); // stop game loop
// show win/lose graphic
var endGameImage;
if (player1Lives <= 0)
{
playSound(LOSE_SOUND_FILE);
endGameImage = preload.getResult("loseImage").result;
}
else if (player2Lives <= 0)
{
endGameImage = preload.getResult("winImage").result;
playSound(WIN_SOUND_FILE);
}
var endGameBitmap = new createjs.Bitmap(endGameImage);
stage.addChild(endGameBitmap);
endGameBitmap.x = (canvas.width / 2) - (endGameImage.width * SCALE_X / 2);
endGameBitmap.y = (canvas.height / 2) - (endGameImage.height * SCALE_Y / 2);
endGameBitmap.scaleX = SCALE_X;
endGameBitmap.scaleY = SCALE_Y;
stage.update();
}
function draw() {
// EaselJS allows for easy updates
stage.update();
}
function playSound(path)
{
var sound = document.createElement("audio");
sound.src = path;
sound.autoplay = true;
}
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// args.setPromise().
};
document.addEventListener("DOMContentLoaded", initialize, false);
app.start();
})();
And there is the problem: when I'm trying to build this game, I'm getting error like this:
0x800a01bd - JavaScript runtime error: Object doesn't support this action
in the marked place in my code
Thanks for any help :)
Three problems, I report them from previous comments:
easeljs not present in the correct folder
mages/Catapults/Red/redFire/redCatapultFire.png missing
change var stage = new createjs.Stage(canvas); to stage = new createjs.Stage(canvas); you don't have to instantiate that variable as reported in the tutorial (http://www.sitepoint.com/creating-a-simple-windows-8-game-with-javascript-game-basics-createjseaseljs/)
And close this other related question (https://stackoverflow.com/a/16101960/975520), I think is solved by posting your solution as answer.
I am trying to get the distance between my character and the ground, I have found something that looks like it should do what I want but it has been written for another version of box2d.
Original:
float targetHeight = 3;
float springConstant = 100;
//make the ray at least as long as the target distance
b2Vec2 startOfRay = m_hovercarBody->GetPosition();
b2Vec2 endOfRay = m_hovercarBody->GetWorldPoint( b2Vec2(0,-5) );
overcarRayCastClosestCallback callback;
m_world->RayCast(&callback, startOfRay, endOfRay);
if ( callback.m_hit ) {
float distanceAboveGround = (startOfRay - callback.m_point).Length();
//dont do anything if too far above ground
if ( distanceAboveGround < targetHeight ) {
float distanceAwayFromTargetHeight = targetHeight - distanceAboveGround;
m_hovercarBody->ApplyForce( b2Vec2(0,springConstant*distanceAwayFromTargetHeight),
m_hovercarBody->GetWorldCenter() );
}
}
I have tried to change it to what I think it should be but it doesn't even call the callback.
var targetHeight = 3;
var springConstant = 100;
//make the ray at least as long as the target distance
startOfRay = new b2Vec2(m_hovercarBody.GetPosition());
endOfRay = new b2Vec(m_hovercarBody.GetWorldPoint( b2Vec2(0,-5)));
function callback(raycast){
if ( raycast.m_hit ) {
var distanceAboveGround = (startOfRay - raycast.m_point).Length();
//dont do anything if too far above ground
if ( distanceAboveGround < targetHeight ) {
var distanceAwayFromTargetHeight = targetHeight - distanceAboveGround;
m_hovercarBody.ApplyForce( b2Vec2(0,springConstant*distanceAwayFromTargetHeight),
m_hovercarBody.GetWorldCenter() );
}
}
}
m_world.RayCast(callback, startOfRay, endOfRay);
Any idea how to convert it to work with box2dweb?
Thanks
It might be that the original bit of code was written for a platform where the coordinate system works differently.
In a Canvas element, the coordinate system starts from the top left corner, meaning that m_hovercarBody.GetWorldPoint( b2Vec2(0,-5)) is checking for a point above the character, rather than below.
I'm not sure about the rest of the code but try changing that to m_hovercarBody.GetWorldPoint( b2Vec2(0,5)) and see what happens.
EDIT:
I think actually the problem is with the way you've structured your callback. Looking up the reference for the Raycast function would reveal more.
(The Javascript version of Box2D you're using is an automatic port of the Actionscript one. Given the two have fairly similar syntax, you can use the reference for Flash.)
The original code you posted seems to be C++, but I don't know much about its syntax. It seems there's some sort of class that does the raycasting (overcarRayCastClosestCallback). You can either look for that, or try and build your own callback function according to the first link I posted. It would be something along the lines of:
function customRaycastCallback(fixture, normal, fraction) {
// you can, for instance, check if fixture belongs to the ground
// or something else, then handle things accordingly
if( /* fixture belongs to ground */ ) {
// you've got the fraction of the original length of the raycast!
// you can use this to determine the distance
// between the character and the ground
return fraction;
}
else {
// continue looking
return 1;
}
}
Try running this on your browser. I coded this when I was learning sensor and RAYCAST in Box2Dweb. Hope it helps.
<html>
<head>
<title>Box2dWeb Demo</title>
</head>
<body>
<canvas id="canvas" width="600" height="420" style="background-color:#333333;" ></canvas>
<div id="cc" style="position:absolute; right:0; top:100px; width:500px; height:50px; margin:0;"></div>
</body>
<script type="text/javascript" src="Box2dWeb-2.1.a.3.js"></script>
<script type="text/javascript" src="jquery-1.7.2.js"></script>
<script type="text/javascript">
var b2Vec2 = Box2D.Common.Math.b2Vec2
, b2BodyDef = Box2D.Dynamics.b2BodyDef
, b2Body = Box2D.Dynamics.b2Body
, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
, b2World = Box2D.Dynamics.b2World
, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
, b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
, b2ContactFilter = Box2D.Dynamics.b2ContactFilter
, b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef
, b2DebugDraw = Box2D.Dynamics.b2DebugDraw
, b2Fixture = Box2D.Dynamics.b2Fixture
, b2AABB = Box2D.Collision.b2AABB
, b2WorldManifold = Box2D.Collision.b2WorldManifold
, b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint
, b2RayCastInput = Box2D.Collision.b2RayCastInput
, b2RayCastOutput = Box2D.Collision.b2RayCastOutput
, b2Color = Box2D.Common.b2Color;
var world = new b2World(new b2Vec2(0,10), true);
var canvas = $('#canvas');
var context = canvas.get(0).getContext('2d');
//box
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(9,7);
bodyDef.userData = 'box';
var fixDef = new b2FixtureDef;
fixDef.filter.categoryBits = 1;
fixDef.density = 10.0;
fixDef.friction = 0.5;
fixDef.restitution = .5;
fixDef.shape = new b2PolygonShape;
fixDef.shape.SetAsBox(1,5);
var box1 = world.CreateBody(bodyDef);
box1.CreateFixture(fixDef);
//circle
var bodyDef2 = new b2BodyDef;
bodyDef2.type = b2Body.b2_dynamicBody;
bodyDef2.position.Set(4,8);
bodyDef2.userData = 'obj';
var fixDef2 = new b2FixtureDef;
fixDef2.filter.categoryBits = 2;
fixDef2.filter.maskBits = 13;
fixDef2.density = 10.0;
fixDef2.friction = 0.5;
fixDef2.restitution = .2;
fixDef2.shape = new b2CircleShape(1);
//circlesensor
var cc = new b2FixtureDef;
cc.shape = new b2CircleShape(2);
cc.shape.SetLocalPosition(new b2Vec2(0 ,0));
cc.density = 0;
cc.isSensor = true;
cc.filter.categoryBits = 8;
var wheel = world.CreateBody(bodyDef2);
wheel.CreateFixture(fixDef2);
wheel.CreateFixture(cc);
//create a ground
var holderDef = new b2BodyDef;
holderDef.type = b2Body.b2_staticBody;
holderDef.userData = "ground";
holderDef.position.Set(10, 14);
var fd = new b2FixtureDef;
fd.filter.categoryBits = 4;
fd.shape = new b2PolygonShape;
fd.shape.SetAsBox(10,1);
var ground = world.CreateBody(holderDef);
ground.CreateFixture(fd);
//create another static body
var holderDef = new b2BodyDef;
holderDef.type = b2Body.b2_staticBody;
holderDef.position.Set(10, 20);
var temp = world.CreateBody(holderDef);
temp.CreateFixture(fd);
var c=0;
$(window).keydown(function(e) {
$('#aa').html(++c);
code = e.keyCode;
if(c==1) {
if(code == 38 && onground)
wheel.SetLinearVelocity(new b2Vec2(0,-10));
if(code == 39)
wheel.ApplyForce(new b2Vec2(1000,0), box1.GetWorldPoint(new b2Vec2(0,0)));
if(code == 37)
wheel.ApplyForce(new b2Vec2(-1000,0), box1.GetWorldPoint(new b2Vec2(0,0)));
}
});
$(window).keyup(function(e) {
c=0;
});
var listener = new Box2D.Dynamics.b2ContactListener;
listener.BeginContact = function(contact) {
if(contact.GetFixtureA().GetBody().GetUserData()== 'obj' || contact.GetFixtureB().GetBody().GetUserData()== 'obj' ) // think about why we don't use fixture's userData directly.
onground = true;// don't put 'var' here!
fxA=contact.GetFixtureA();
fxB=contact.GetFixtureB();
sA=fxA.IsSensor();
sB=fxB.IsSensor();
if((sA && !sB) || (sB && !sA)) {
if(sA) {
$('#cc').prepend(contact.GetFixtureB().GetBody().GetUserData() + ' is in the viscinity of body '+contact.GetFixtureA().GetBody().GetUserData()+'<br>');
}
else {
$('#cc').prepend(contact.GetFixtureA().GetBody().GetUserData() + ' is in the viscinity of body '+contact.GetFixtureB().GetBody().GetUserData()+'<br>');
}
}
}
listener.EndContact = function(contact) {
if (contact.GetFixtureA().GetBody().GetUserData()== 'obj' || contact.GetFixtureB().GetBody().GetUserData()== 'obj' )
onground = false;
}
var debugDraw = new b2DebugDraw();
debugDraw.SetSprite ( document.getElementById ("canvas").getContext ("2d"));
debugDraw.SetDrawScale(30); //define scale
debugDraw.SetAlpha(1);
debugDraw.SetFillAlpha(.3); //define transparency
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
world.SetDebugDraw(debugDraw);
window.setInterval(update,1000/60);
//mouse
var mouseX, mouseY, mousePVec, isMouseDown, selectedBody, mouseJoint;
var canvasPosition = getElementPosition(document.getElementById("canvas"));
document.addEventListener("mousedown", function(e) {
isMouseDown = true;
handleMouseMove(e);
document.addEventListener("mousemove", handleMouseMove, true);
}, true);
document.addEventListener("mouseup", function() {
document.removeEventListener("mousemove", handleMouseMove, true);
isMouseDown = false;
mouseX = undefined;
mouseY = undefined;
}, true);
function handleMouseMove(e) {
mouseX = (e.clientX - canvasPosition.x) / 30;
mouseY = (e.clientY - canvasPosition.y) / 30;
};
function getBodyAtMouse() {
mousePVec = new b2Vec2(mouseX, mouseY);
var aabb = new b2AABB();
aabb.lowerBound.Set(mouseX - 0.001, mouseY - 0.001);
aabb.upperBound.Set(mouseX + 0.001, mouseY + 0.001);
// Query the world for overlapping shapes.
selectedBody = null;
world.QueryAABB(getBodyCB, aabb);
return selectedBody;
}
function getBodyCB(fixture) {
if(fixture.GetBody().GetType() != b2Body.b2_staticBody) {
if(fixture.GetShape().TestPoint(fixture.GetBody().GetTransform(), mousePVec)) {
selectedBody = fixture.GetBody();
return false;
}
}
return true;
}
//at global scope
var currentRayAngle = 0;
var input = new b2RayCastInput();
var output = new b2RayCastOutput();
var b = new b2BodyDef();
var f = new b2FixtureDef();
var closestFraction = 1;
var intersectionNormal = new b2Vec2(0,0);
var intersectionPoint = new b2Vec2();
rayLength = 25; //long enough to hit the walls
var p1 = new b2Vec2( 11, 7 ); //center of scene
var p2 = new b2Vec2();
var normalEnd = new b2Vec2();
function update() {
if(isMouseDown && (!mouseJoint)) {
var body = getBodyAtMouse();
if(body) {
var md = new b2MouseJointDef();
md.bodyA = world.GetGroundBody();
md.bodyB = body;
md.target.Set(mouseX, mouseY);
md.collideConnected = true;
md.maxForce = 300.0 * body.GetMass();
mouseJoint = world.CreateJoint(md);
body.SetAwake(true);
}
}
if(mouseJoint) {
if(isMouseDown) {
mouseJoint.SetTarget(new b2Vec2(mouseX, mouseY));
} else {
world.DestroyJoint(mouseJoint);
mouseJoint = null;
}
}
world.Step(1 / 60, 10, 10);
world.DrawDebugData();
world.ClearForces();
world.SetContactListener(listener);
ray();
};
function ray() {
//in Step() function
var k = 360/20;
var t = k/60;
var DEGTORAD = Math.PI/180;
currentRayAngle += t * DEGTORAD; //one revolution every 20 seconds
//console.log(currentRayAngle*(180/Math.PI));
//calculate points of ray
p2.x = p1.x + rayLength * Math.sin(currentRayAngle);
p2.y = p1.y + rayLength * Math.cos(currentRayAngle);
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
closestFraction = 1;
var b = new b2BodyDef();
var f = new b2FixtureDef();
for(b = world.GetBodyList(); b; b = b.GetNext()) {
for(f = b.GetFixtureList(); f; f = f.GetNext()) {
if(!f.RayCast(output, input))
continue;
else if(output.fraction < closestFraction) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
intersectionPoint.x = p1.x + closestFraction * (p2.x - p1.x);
intersectionPoint.y = p1.y + closestFraction * (p2.y - p1.y);
normalEnd.x = intersectionPoint.x + intersectionNormal.x;
normalEnd.y = intersectionPoint.y + intersectionNormal.y;
context.strokeStyle = "rgb(255, 255, 255)";
context.beginPath(); // Start the path
context.moveTo(p1.x*30,p1.y*30); // Set the path origin
context.lineTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path destination
context.closePath(); // Close the path
context.stroke();
context.beginPath(); // Start the path
context.moveTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path origin
context.lineTo(normalEnd.x*30, normalEnd.y*30); // Set the path destination
context.closePath(); // Close the path
context.stroke(); // Outline the path
}
//helpers
//http://js-tut.aardon.de/js-tut/tutorial/position.html
function getElementPosition(element) {
var elem=element, tagname="", x=0, y=0;
while((typeof(elem) == "object") && (typeof(elem.tagName) != "undefined")) {
y += elem.offsetTop;
x += elem.offsetLeft;
tagname = elem.tagName.toUpperCase();
if(tagname == "BODY")
elem=0;
if(typeof(elem) == "object") {
if(typeof(elem.offsetParent) == "object")
elem = elem.offsetParent;
}
}
return {x: x, y: y};
}
</script>
</html>
Here's a Raycast in Box2dWeb that I got working:
var p1 = new b2Vec2(body.GetPosition().x, body.GetPosition().y); //center of scene
var p2 = new b2Vec2(body.GetPosition().x, body.GetPosition().y + 5); //center of scene
world.RayCast(function(x){
console.log("You've got something under you");
}, p1,p2);