I am using multiatlas and json file for my sprites in Phaser 3. How can I set an offset (like setOrigin) for specific frames?
There are some properties in the json which I suppose may help, but I don't understand their meaning. What's the difference between sourceSize and spriteSourceSize? I have also seen anchor property, but I suspect it's an old Phaser version...
The best alternative would be if it was possible to set the frame offset in the animation definition itself, and not in the json definition of frames. But is it possible?
EDIT: nevermind, I misunderstood the question. It's about animation frames and setOrigin, and wether the setOrigin can be different for different frames. AFIAK all animation sprites get the same origin, so the same "anchor", but I'm not 100% sure about this.
v--old answer--v
Wait, are you storing the animation-sequence in the sprite json file? If so, I'd like to know how you do that, because AFAIK you have to create the animation sequences programmatically in JavaScript, I usually do that in the preloader:
// preloader scene
create: function ()
{
// create animations
this.anims.create({
key: 'anim_run',
frames: [
{ key: 'sprites', frame: 'guyrun1' },
{ key: 'sprites', frame: 'guyrun2' },
{ key: 'sprites', frame: 'guyrun3' },
{ key: 'sprites', frame: 'guyrun2' }
],
frameRate: 15,
repeat: -1
});
//..etc.
},
And you can start the animation with a startFrame parameter to offset the animation, i.e. start at a frame other than the first frame, like so
// start animation with random offset
var r = Phaser.Math.RND.between(0, 3);
mainguy.play('anim_run', null, r); // key, ignoreIfPlaying = null, startFrame = r
Related
I want to load an image collection tileset into my phaser game. I know that with tilesets that are just one image you can just load that image into phaser, but what about an image collection? In Tiled I saw the options to export that tileset as either .tsx or .json, but I don't know if that helps in my case. The reason I need this is because I have some objects that are too big to be used as tiles. I can load them into Tiled and place them like I want to, but obviously they don't show up in my game, unless I can import that tileset into phaser. Does anyone know how to do that, or maybe you know a better option than using an image collection?
Well after some tests, and updating my Tiled version to 1.9.2, it seems there is an pretty simple way.
As long as the tileset collection is marked as "Embeded in map"
(I could have sworn, this checkbox was hidden/deactivated, when selecting "Collection of Images", in my early Tiled version)
Export the map as json
Load map and tile-images
preload() {
this.load.tilemapTiledJSON('map', 'export.tmj');
this.load.image('tile01', 'tile01.png');
this.load.image('tile02', 'tile02.png');
...
}
create Phaser TileSets, just use the filename from the json as the tilsetName (this is the "tricky" part, atleast for me)
create() {
var map = this.make.tilemap({ key: 'map' });
var img1 = map.addTilesetImage( 'tile01.png', 'tile01');
var img2 = map.addTilesetImage( 'tile02.png', 'tile02');
...
// create the layer with all tilesets
map.createLayer('Tile Layer 1', [img1, img2, ...]);
...
}
This should work, atleast with a "Collection of images", with images that have a size of 8x8 pixel (since I don't know the needed/intended Images size, I didn't want to waste time testing various images-sizes needlessly).
Here a small demo:
(due to CORS-issues the map data is inserted as jsonObject and the textures are generate and not loaded)
const mapJsonExport = {"compressionlevel":-1,"height":10,"infinite":false,"layers":[{"compression":"","data":"AQAAAAEAAAACAAAAAgAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAEAAAACAAAAAgAAAAEAAAABAAAAAgAAAAIAAAABAAAAAgAAAAIAAAACAAAAAgAAAAEAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAACAAAAAgAAAAEAAAACAAAAAgAAAAEAAAACAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAIAAAACAAAAAgAAAAEAAAABAAAAAgAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAEAAAACAAAAAQAAAA==","encoding":"base64","height":10,"id":1,"name":"Tile Layer 1","opacity":1,"type":"tilelayer","visible":true,"width":10,"x":0,"y":0}],"nextlayerid":2,"nextobjectid":1,"orientation":"orthogonal","renderorder":"right-down","tiledversion":"1.9.2","tileheight":8,"tilesets":[{"columns":0,"firstgid":1,"grid":{"height":1,"orientation":"orthogonal","width":1},"margin":0,"name":"tiles","spacing":0,"tilecount":2,"tileheight":8,"tiles":[{"id":0,"image":"tile01.png","imageheight":8,"imagewidth":8},{"id":1,"image":"tile02.png","imageheight":8,"imagewidth":8}],"tilewidth":8}],"tilewidth":8,"type":"map","version":"1.9","width":10};
var config = {
width: 8 * 10,
height: 8 * 10,
zoom: 2.2,
scene: { preload, create }
};
function preload() {
// loading inline JSON due to CORS-issues with the code Snippets
this.load.tilemapTiledJSON('map', mapJsonExport);
// generating texture instead of loading them due to CORS-issues with the code Snippets
let graphics = this.make.graphics({add: false});
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 8, 8);
graphics.generateTexture('tile01', 8, 8);
graphics.fillStyle(0x000000);
graphics.fillRect(0, 0, 8, 8);
graphics.generateTexture('tile02', 8, 8);
}
function create () {
let map = this.make.tilemap({ key: 'map' });
let img1 = map.addTilesetImage( 'tile01.png', 'tile01');
let img2 = map.addTilesetImage( 'tile02.png', 'tile02');
map.createLayer('Tile Layer 1', [img1, img2], 0, 0);
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I am using Chart.js to map an aircraft's vertical approach path:
The code to get the actual approach path is relatively straightforward:
var position_reports = [
{
altitude: 4606
heading: 42.94507920742035
latitude: 35.16972222222222
longitude: -101.75638888888889
speed: 133
vertical_speed: -714},
},
// additional position reports redacted for clarity
];
var data = [];
for(var x = 0; x < position_reports.length; x++) {
data.push(position_reports[x].altitude);
}
// at this point, data looks like this: [4606, 4605, 4604, 4603, 4603, 4602, etc....]
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Actual Altitude',
borderColor: '#ff6600',
data: data,
borderWidth: 5,
fill: false,
}
]
},
// additional options redacted for clarity
});
This above aircraft's glidepath (mostly) follows a standard 3-degree descent path. I would like to draw a fixed 4-degree and 2-degree line so I can make sure the aircraft's actual vertical path falls between 4 and 2 degrees. Hand drawn (and thus not to scale) example of what I'm trying to achieve:
How to I go about adding these red lines into Chart.js, relative to the vertical path? Is it possible with this library, or should I be using something else? Any feedback appreciated!
You can always write a custom plugin to do it, but I think this plugin of chart.js itself can help you out looking at the example. If you specify the right start and end it should draw a straight line.
https://github.com/chartjs/chartjs-plugin-annotation#line-annotations
Basically the problem is that after you rotate the camera, the points that are given as arguments in the callback for dragging are not what I expected. I'm guessing I have to Rotate the given points also but I just couldn't.
Can Someone explain what is going on, is this some kind of bug or what should I do in order the sprite to follow the mouse cursor?
Easiest way to explain the problem is to reproduce it:
1) Go to Phaser Example Runner
2) Copy- Paste this code:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
function create ()
{
var image = this.add.sprite(200, 300, 'eye').setInteractive();
this.cameras.main.setRotation(Math.PI);
image.on('pointerover', function () {
this.setTint(0x00ff00);
});
image.on('pointerout', function () {
this.clearTint();
});
this.input.setDraggable(image);
this.input.on('dragstart', function (pointer, gameObject) {
gameObject.setTint(0xff0000);
});
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
console.log(`x: ${dragX}, y: ${dragY}`);
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function (pointer, gameObject) {
gameObject.clearTint();
});
}
3) Open the console, drag around the Eye and look at what coordinates are given.
4) If you remove line 24 (the rotation of the camera) Everything works as expected.
(The example is taken from Phaser 3 Official examples and a bit changed for the bug)
According to Phaser's API Documentation on the setRotation() method, the rotation given in radians applies to everything rendered by the camera. Unfortunately, your pointer isn't rendered by the camera so it doesn't get the same rotated coordinates. Not sure if this is a bug with the library or just a poorly documented exception, but I believe there is a workaround.
Create 2 variables to hold an initial position and a final position:
var image = this.add.sprite(200, 300, 'eye').setInteractive();
var initial = [];
var final = [];
Populate the initial position in your .on('dragstart') method:
this.input.on('dragstart', function (pointer, gameObject) {
initial = [
gameObject.x,
gameObject.y,
pointer.x,
pointer.y
];
gameObject.setTint(0xff0000);
});
Then, populate the final variable in your .on('drag') method:
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
final = [
gameObject.x, // not necessary but keeping for variable shape consistency
gameObject.y, // not necessary but keeping for variable shape consistency
pointer.x,
pointer.y
];
gameObject.x = initial[0] + (initial[2] - final[2]);
gameObject.y = initial[1] + (initial[3] - final[3]);
});
All we're doing here is keeping track of the change in pointer position and mimicking that change in our gameObject.
I want to create a card, once clicked on, it rotates 180°.
So I want to create a card component, that has a plane for the back side and a plane for the front side.
Once clicked on the card-entity, it rotates both planes, so that is looks like the card is turning around.
So here's what I got so far:
custom component: Card, gets added X-times.
A cursor detects a mouseEnter, then I want to card to rotate.
On mouseLeave, the card rotates back to the original state.
So, how can I achieve this the best?
Multiple meshes in a custom component or something?
AFRAME.registerComponent('card', {
schema: {
material: {type:'selector'},
hoverMaterial: {type: 'selector'},
card: {type: 'selector'}
},
multiple: true,
element: null,
init: function () {
console.log("Card loaded");
var data = this.data;
this.el.setAttribute('material', 'src', data.material);
this.el.addEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.addEventListener('mouseleave', this.onMouseLeave.bind(this));
},
onMouseEnter: function() {
this.el.removeEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.setAttribute('material', 'src', this.data.hoverMaterial);
this.el.setAttribute('rotation', '0 180 0');
},
onMouseLeave: function () {
//this.el.addEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.setAttribute('material', 'src', this.data.material);
this.el.setAttribute('rotation', '0 0 0');
},
update: function (oldData) {
},
remove: function () {
this.el.removeObject3D('mesh');
}
});
Create a custom component that appends two child entities to itself with the appropriate orientations and materials - for front and back sides.
You could also add a transparent plane mesh to the component, to enable raycasting.
Reference to the HTML node is this.el and you can manipulate it like a regular HTML node.
Code sample:
AFRAME.registerComponent('card', {
// ...
addChildren: function () {
this.frontFace = document.createElement('a-entity');
this.backFace = document.createElement('a-entity');
// ...
this.el.appendChild(this.frontFace);
this.el.appendChild(this.backFace);
}
// ...
For the input use either a cursor component or the raycaster component itself. Note that you have to have a mesh on the component you are trying to use in raycasting.
How can background image be set in repeat effect in cocos creater ?
If character runs, back image should be flow backwards, but I want to background repeatedly continuously repeated by using one device size image file.
I think you can do this by using two background images, placed one after the other, with anchors 0,0, size assumed to be same as canvas size (Images not placed inside the canvas). This script applied to both images can achieve that effect, in simplest form.
cc.Class({
extends: cc.Component,
properties: {
// speed of scrolling
speed: 0,
// reset to position
bgwidth: 0
},
// use this for initialization
onLoad: function () {
},
// called every frame, uncomment this function to activate update callback
update: function (dt) {
var x = this.node.x;
x -= this.speed * dt;
if (x <= -this.bgwidth) {
x += this.bgwidth*2;
}
this.node.x = x;
},
});