I'm trying to achieve this sort of effect, but by using JS and on device orientation change (with gyroscope). So I wrote this code:
var sky, flag, objects, maxPositiveTranslateFar = 30, maxPositiveTranslateNear = 20, maxNegativeTranslateFar=-30, maxNegativeTranslateNear=-20;
document.addEventListener("DOMContentLoaded",onload);
function onload(){
sky = document.getElementById('sky');
flag = document.getElementById('flag');
objects = [{objectName : sky}, {objectName : flag}];
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', function(eventData){
var tiltLR = eventData.gamma;
var tiltD = eventData.beta;
deviceOrientationHandler(tiltLR, tiltD);
}, false);
} else {
// Do something
}
}
function deviceOrientationHandler(tiltLR, tiltD) {
for(var i = 0; i <= objects.length - 1; i++){
if(objects[i].objectName == flag){
if(tiltLR > maxPositiveTranslateNear){
tiltLR = maxPositiveTranslateNear;
} else if(tiltLR < maxNegativeTranslateNear) {
tiltLR = maxNegativeTranslateNear;
}
flag.style.webkitTransform = "translateX("+tiltLR+"px)";
} else {
if(tiltLR*1.7 > maxPositiveTranslateFar){
tiltLR = maxPositiveTranslateFar;
} else if(tiltLR*1.7 < maxNegativeTranslateFar) {
tiltLR = maxNegativeTranslateFar;
}
if(tiltD > maxPositiveTranslateNear){
tiltD = maxPositiveTranslateNear;
} else if(tiltD < maxNegativeTranslateNear) {
tiltD = maxNegativeTranslateNear;
}
sky.style.webkitTransform = "translateX("+(tiltLR*1.7)+"px) translateY("+tiltD+"px)";
}
}
}
It works, the sky and flag elements are moving correspondingly, however the movement isn't smooth as in the example above. I guess it's because the values are constantly changing when the device is rotated, thus the deviceorientationevent keeps firing. I tried to add transition to these elements' CSS:
-webkit-transition: -webkit-transform 1s ease-out;
But it just made things worse - the transform became really jumpy.
Does anybody have a solution how to make this transformation smooth? I have to do it with plain JS/CSS only, no external JS libraries (or by using a library, but it must very light-weight, 40-50 kb. is the max).
Related
I'm working on a 3D web app where I want to use positional 3D audio.
I started noticing that a crackling noise appears on the output as the sound source changes its position.
Initially I thought it could be a programming issue or a library issue (I was using howler.js).
I made a very basic example based on plain JS and Webaudio API which is shown here
let params={
"xPosition":0,
"zPosition":-1
};
let gui=new dat.GUI( { autoPlace: true, width: 500 });
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
let panner;
let listener;
let source;
let osc;
function initWebAudio(){
audioCtx = new AudioContext();
panner = audioCtx.createPanner();
listener = audioCtx.listener;
osc = audioCtx.createOscillator();
osc.frequency.value = 70;
osc.connect(panner);
osc.start(0);
panner.connect(audioCtx.destination);
panner.panningModel = 'HRTF';
panner.distanceModel = 'linear';
panner.maxDistance = 60;
panner.refDistance = 1;
panner.rolloffFactor = 1;
panner.coneInnerAngle = 360;
panner.coneOuterAngle = 360;
panner.coneOuterGain = 0;
panner.positionX.setValueAtTime(0,audioCtx.currentTime);
panner.positionY.setValueAtTime(1,audioCtx.currentTime);
panner.positionZ.setValueAtTime(1,audioCtx.currentTime);
if(panner.orientationX) {
panner.orientationX.value = 1;
panner.orientationY.value = 0;
panner.orientationZ.value = 0;
} else {
panner.setOrientation(1,0,0);
}
if(listener.forwardX) {
listener.forwardX.value = 0;
listener.forwardY.value = 0;
listener.forwardZ.value = -1;
listener.upX.value = 0;
listener.upY.value = 1;
listener.upZ.value = 0;
} else {
listener.setOrientation(0,0,-1,0,1,0);
}
if(listener.positionX) {
listener.positionX.value = 0;
listener.positionY.value = 0;
listener.positionZ.value = 0;
} else {
listener.setPosition(0,0,0);
}
}
function positionPanner() {
if(panner.positionX) {
panner.positionX.setValueAtTime(params.xPosition, audioCtx.currentTime);
} else {
panner.setPosition(params.xPosition,0,params.zPosition);
}
}
function tick(){
positionPanner();
}
function onClickStart(){
initWebAudio();
gui.add(osc.frequency,"value",50,220).name("frequency");
setInterval(tick,50);
}
function buildMenu(){
gui.add(params,"xPosition",-3,3).step(0.001);
gui.add(window,"onClickStart").name("start");
}
buildMenu();
https://jsfiddle.net/fedeM75/t9vpm8so/23/
Press start, then as you change the xPosition slider a crackling sound appears.
It is specially noticeable using headphones.
I searched on google and some people say that it has to do with the rate of change of the position. But I tried with different value ranges and it still happens with small changes.
By the way if the condition to use the panner is to have a very slow rate of change in the position it does not seem to be useful in real world cases.
I use a timer to update the position but the same happens using requestAnimationFrame()
Does anyone has a clue on why this happens and how to solve it?
I had this crackling audio with a simple GainNode. It could be a bug indeed since a simple implementation in the ScriptProcessor does work. Or it's the fact that I/we just don't get how the timing with audioCtx.currentTime should be handled?
For me the solution was to ditch the gain node and use a ScriptProcessor (deprecated: you could/should use an AudioWorkletNode) with my own gain applied to the audio signal directly:
let myGain = 1.0; // change this as you like without crackling noises
let whiteNoise = audioCtx.createScriptProcessor(4096, 0, 1);
whiteNoise.onaudioprocess = function(e) {
let output = e.outputBuffer.getChannelData(0);
for (let i = 0; i < output.length; i++) {
output[i] = (Math.random() * 2 - 1) * myGain;
}
}
This won't fix your problem directly since you have this problem with the 3D panner, but perhaps you can find sourcecode/examples for such a panner implementation and port it to an AudioWorkletNode? Hope this helps.
I have 3 clickable objects. When one is clicked, this becomes the 'selected unit'.
I am trying to create some generic actions for the units such as moving to a point.
In my create function I initialize the units, when a unit is clicked on - this is supposed to become the 'selected unit' so that my movement and direction function applies to the this unit. However, the script is not able to recognize which unit intend for example I get this error:
Uncaught TypeError: Cannot read property 'velocity' of undefined.
Is there a way to use a variable to indicate selected users and pass that to the functions?
window.onload = function() {
var block_count = 0;
var block = '';
var selected_unit = '';
var unit_clicked = 0;
var tank1 = null;
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update, render: render});
function preload () {
game.load.image('block', 'block.png');
game.load.image('tank1', 'tank.png');
game.load.image('baddie', 'tank.png');
game.load.image('mouse_btn', 'block.png');
game.input.mouse.capture = true;
}
function create () {
game.physics.startSystem(Phaser.Physics.ARCADE);
mouse_btn = game.add.sprite(30,30, 'mouse_btn');
mouse_btn.anchor.setTo(0.5, 0.5);
//T1
tank1 = game.add.sprite(30,30, 'tank1');
initialise_player(tank1);
game.physics.enable(tank1, Phaser.Physics.ARCADE);
//T2
tank2 = game.add.sprite(30,60, 'tank1');
initialise_player(tank2);
game.physics.enable(tank2, Phaser.Physics.ARCADE);
game.world.setBounds(0, 0, 2000, 2000);
game.camera.follow(tank1);
}
function update () {
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
if(game.input.activePointer.leftButton.isDown && block_count == 0 && unit_clicked == 0) {
game.input.activePointer.leftButton.stop(event);
block_count =1;
block = game.add.sprite(game.input.mousePointer.worldX, game.input.mousePointer.worldY, 'block');
game.physics.enable(block, Phaser.Physics.ARCADE);
block.anchor.setTo(0.5, 0.5)
lookAtObject(selected_unit, block, 0.005);
}
if(block.alive){
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
} else {
console.log(selected_unit)
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
if(game.physics.arcade.collide(selected_unit, block)) {
block_count--;
block.kill();
}
}
function render(){
//console.log(game.physics.arcade.collide(tank1, block))
}
function lookAtObject(obj, target, rotspeed){
var angle = Math.atan2(block.y - tank1.y, block.x - tank1.body.x);
tank1.rotation = angle + game.math.degToRad(90);
}
function initialise_player(tank1){
tank1.anchor.setTo(0.5, 0.5);
tank1.inputEnabled = true;
tank1.input.useHandCursor = true;
tank1.events.onInputDown.add(t1_clicked,this);
tank1.events.onInputOver.add(t1_over, this)
tank1.events.onInputOut.add(t1_out, this)
}
function t1_clicked() {
selected_unit = tank1;
}
function t1_over() {
unit_clicked = 1
}
function t1_out () {
unit_clicked = 0
}
};
The error you're getting on initial load is because in update you're making an assumption that selected_unit exists and has a body.
Update your third if to make sure selected_unit is defined.
if (selected_unit !== '') {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
However, a better option would be to put this a few lines down, where you kill the block instead.
if(game.physics.arcade.collide(selected_unit, block)) {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
block_count--;
block.kill();
}
if (block.alive); moveToObject is also expecting selected_unit to exist and have a body, which may not be the case; wrap it with a check.
if (selected_unit !== '') {
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
}
That now allows tank1 to rotate to look at the item you just placed, but it doesn't move it until it or tank2 have been clicked on.
This also points out that there are a number of tweaks you'll want to make to your code in general, since you're ignoring arguments that are being passed in. For example, t1_clicked isn't using the sprite that's been clicked on, but is instead just hard-coding tank1. lookAtObject isn't using obj or target, but again has values hard-coded in.
One other thing you may want to change is the following:
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
If you make that the following, you won't end up with an extra sprite hanging about on the screen.
if (block_count === 0) {
mouse_btn.x = game.input.mousePointer.worldX;
mouse_btn.y = game.input.mousePointer.worldY;
}
I'm trying to create a small game that has a soldier moving around a small area and fires bullets. I'm a new programmer who just started javascript not but a few months ago, and so I apologize if my question is poorly worded or my code is clunky.
I've tried using [group].get(i) to isolate each individual sprite that comes out of the gun, but it simply breaks and crashes the program.
Here is all of the code for my (small) program:
var soldier = createSprite(200, 200);
soldier.setAnimation("soldier_still");
var bullet = createGroup();
function draw() {
background(rgb(100, 85, 45));
move();
attack();
drawSprites();
}
function attack() {
if (mouseWentDown("leftButton")) {
for (var i = 0; i < 3; i++) {
bullet.add(createSprite(soldier.x, soldier.y));
bullet.setAnimationEach("bullet");
bullet.pointToEach(World.mouseX, World.mouseY);
bullet.setLifetimeEach(50);
bullet.setSpeedAndDirectionEach(20, World.mouseX, World.mouseY);
}
}
}
function move() {
createEdgeSprites();
World.allSprites.bounceOff(topEdge);
World.allSprites.bounceOff(bottomEdge);
World.allSprites.bounceOff(leftEdge);
World.allSprites.bounceOff(rightEdge);
soldier.pointTo (World.mouseX,World.mouseY);
if (keyDown() == false) {
soldier.setAnimation("soldier_still");
soldier.velocityX = 0;
soldier.velocityY = 0;
}
if (keyDown("up")) {
soldier.setAnimation("soldier_move");
soldier.velocityY = -5;
}
if (keyDown("down")) {
soldier.setAnimation("soldier_move");
soldier.velocityY = 5;
}
if (keyDown("left")) {
soldier.setAnimation("soldier_move");
soldier.velocityX = -5;
}
if (keyDown("right")) {
soldier.setAnimation("soldier_move");
soldier.velocityX = 5;
}
}
The program either crashes, or all bullet sprites face the mouse's position. Is there anyway I can improve my code, or a solution to making each group (bullet) sprite its own individual sprite?
I'm trying to create a fire effect for a game in Matter.js, and I need to blur a circle to make it look more realistic. However, I need to make it so it only blurs the fire, not the whole canvas. How can I do this?
This is the code I have so far:
function setOnFire(object) {
var fireX = object.position.x;
var fireY = object.position.y;
var fire = Bodies.circle(fireX, fireY, vw*1, {
isStatic: true,
render: {
fillStyle: "rgba(255,130,0,1)"
}
});
World.add(world, fire);
}
This is not exactly what I had in mind, but it is as close as you can get.
Start by going to matter.js and go to this section:
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
part = body.parts[k];
if (!part.render.visible)
continue;
Add this code after the continue;:
if (body.bloom) {
c.shadowColor = body.render.fillStyle;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = body.bloom;
}
Then, go to the very end of the loop and add this:
if (body.bloom) {
c.shadowColor = "transparent";
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = 0;
}
Then, just add the bloom while making your body. For instance:
let fireParticle = Bodies.circle(0, 0, {
bloom: 25
});
Well i have this SVG canvas element, i've got to the point so far that once a user clicks and drags the canvas is moved about and off-screen elements become on screen etc....
However i have this is issue in which when ever the user then goes and click and drags again then the translate co-ords reset to 0, which makes the canvas jump back to 0,0.
Here is the code that i've Got for those of you whio don't wanna use JS fiddle
Here is the JSfiddle demo - https://jsfiddle.net/2cu2jvbp/2/
edit: Got the solution - here is a JSfiddle DEMO https://jsfiddle.net/hsqnzh5w/
Any and all sugesstion will really help.
var states = '', stateOrigin;
var root = document.getElementById("svgCanvas");
var viewport = root.getElementById("viewport");
var storeCo =[];
function setAttributes(element, attribute)
{
for(var n in attribute) //rool through all attributes that have been created.
{
element.setAttributeNS(null, n, attribute[n]);
}
}
function setupEventHandlers() //self explanatory;
{
setAttributes(root, {
"onmousedown": "mouseDown(evt)", //do function
"onmouseup": "mouseUp(evt)",
"onmousemove": "mouseMove(evt)",
});
}
setupEventHandlers();
function setTranslate(element, x,y,scale) {
var m = "translate(" + x + "," + y+")"+ "scale"+"("+scale+")";
element.setAttribute("transform", m);
}
function getMousePoint(evt) { //this creates an SVG point object with the co-ords of where the mouse has been clicked.
var points = root.createSVGPoint();
points.x = evt.clientX;
points.Y = evt.clientY;
return points;
}
function mouseDown(evt)
{
var value;
if(evt.target == root || viewport)
{
states = "pan";
stateOrigin = getMousePoint(evt);
console.log(value);
}
}
function mouseMove(evt)
{
var pointsLive = getMousePoint(evt);
if(states == "pan")
{
setTranslate(viewport,pointsLive.x - stateOrigin.x, pointsLive.Y - stateOrigin.Y, 1.0); //is this re-intializing every turn?
storeCo[0] = pointsLive.x - stateOrigin.x
storeCo[1] = pointsLive.Y - stateOrigin.Y;
}
else if(states == "store")
{
setTranslate(viewport,storeCo[0],storeCo[1],1); // store the co-ords!!!
stateOrigin = pointsLive; //replaces the old stateOrigin with the new state
states = "stop";
}
}
function mouseUp(evt)
{
if(states == "pan")
{
states = "store";
if(states == "stop")
{
states ='';
}
}
}
In your mousedown function, you are not accounting for the fact that the element might already have a transform and you are just overwriting it.
You are going to need to either look for, and parse, any existing transform. Or an easier approach would be to keep a record of the old x and y offsets and when a new mousedown happens add them to the new offset.