I'm building an HTML5 game with Phaser 3. I have a counter tween that I add to the scene upon a certain if condition being met on click of a game object. (It is a text value that flashes red if the card's maximum stat value is lower than the player's value of that stat.)
First, I set the variable based on which card was clicked:
if (this == action1_card) {
action = action1
energy_cost = action1_energy_cost
max_suspicion = action1_suspicion_threshold
selected_card = action1_card_selected
} else if (this == action2_card) {
action = action2
energy_cost = action2_energy_cost
max_suspicion = action2_suspicion_threshold
selected_card = action2_card_selected
}
Once I have the values I want (max suspicion is the relevant variable in this case), I then apply the flashing color animation to it if the condition is met.
if (action.suspicion_threshold < game_data.player.stats.suspicion) {
t.tweens.addCounter({
from: 0,
to: 100,
duration: 250,
repeat: 1,
yoyo: true,
ease: Phaser.Math.Easing.Sine.InOut,
onUpdate: function(tween) {
const value = Math.floor(tween.getValue());
const colorObject = Phaser.Display.Color.Interpolate.ColorWithColor(
Phaser.Display.Color.ValueToColor('#000000'),
Phaser.Display.Color.ValueToColor('#FF0000'),
100,
value
)
const color = Phaser.Display.Color.RGBToString(colorObject.r, colorObject.g, colorObject.b)
max_suspicion.setColor(color);
}
});
The issue is that, if I was to click on card 1 and then card 2 before the animation was finished on card 1, the animation from card 1's text would resume on card 2. This makes sense, as the variable "max_suspicion" was reassigned to the text object on card 2, and the tween's onUpdate function is continuing as it should.
How do I either:
1) prevent this from being reassigned (e.g. setting max_suspicion to permanently represent card 1's text object for the scope of the tween)
or
2) reset card 1's value to it's original color and end the tween prematurely so it doesn't carry over to card 2?
I will gladly clarify anything as needed. Thank you so much for any help on this!
It depends on your code, what the best solution is. (and ofcourse the desired gameplay)
I personally would put the tween in a variable:
this.mytween = scene.tweens.addCounter(...);
and stop it on click before i do anything else
if(this.mytween) {// just checking if it exists
this.mytween.stop();
... // may be reset the color
}
Related
I started this project just for fun (inspired by the Conway's Game of Life). In my project, I have a file for the cell (defined as an object) and other for the game (universe) where the rules are set so the cell can play. The cell object is defined like this:
const cell = {
x_birth: 0,
y_birth: 0,
alive: false,
live: function() {
fill(100);
square((WIDTH/F)*x, (HIGH/F)*y, (WIDTH/F), (HIGH/F));
},
die: function() {
fill(255);
square((WIDTH/F)*x, (HIGH/F)*y, (WIDTH/F), (HIGH/F));
},
move: function(x_var, y_var) {
if (this.alive == false){
x = this.x_birth;
y = this.y_birth;
this.alive = true; // Bug?
this.live();
} else { //... }}}
WIDTH, HIGH and F are global constants from the other main file.
The problem I have is that when I create two objects with 'Object.create(cell)' in the main file, just one of the cells moves. It appears that when one of the cells executes its function 'move()', it does it correctly, setting it's property 'alive' to 'true' when the other cell (object) doesn't. I though that this may be the problem, but then I searched and I didn't find why this should be the issue.
If you want, you can download the files from my repo: https://github.com/Atotem/Game.git . There you will understand what i mean. I'm open to all suggestions.
PD: I'm using p5.js libraries.
I am writing a Figma plugin to generate a random colour and modify the fill of a selection.
This works fine when the selection node has a fill. But when there is no fill I get an error when trying to apply fills[0].color = newColor;.
When logging the fill on that node I get [] which I'm assuming is an empty array.
Figma nodes can have multiple fills and requires the format node.fills[1].color when assigning values.
So how can I create a color assignment for a node where there is an empty array?
import chroma from '../node_modules/chroma-js/chroma'
import clone from './clone'
for (const node of figma.currentPage.selection) {
if ("fills" in node) {
const fills = clone(node.fills);
// Get a random colour from chroma-js.
const random = chroma.random().gl();
// Create an array that matches the fill structure (rgb represented as 0 to 1)
const newColor = {r: random[0], g: random[1], b: random[2]};
// Only change the first fill
fills[0].color = newColor;
// Replace the fills on the node.
node.fills = fills;
}
}
// Make sure to close the plugin when you're done. Otherwise the plugin will
// keep running, which shows the cancel button at the bottom of the screen.
figma.closePlugin();
I have a solution (not necessarily correct).
It looks like I need to check if the original node has an array and if not, create a complete object within an array. I think I had the structure wrong when I attempted this before.
import chroma from '../node_modules/chroma-js/chroma'
import clone from './clone'
for (const node of figma.currentPage.selection) {
if ("fills" in node) {
let fills;
// Check to see if the initial node has an array and if it's empty
if (Array.isArray(node.fills) && node.fills.length) {
// Get the current fills in order to clone and modify them
fills = clone(node.fills);
} else {
// Construct the fill object manually
fills = [{type: "SOLID", visible: true, opacity: 1, blendMode: "NORMAL", color: {}}]
}
// Get a random colour from chroma-js.
const random = chroma.random().gl();
// Create an array that matches the fill structure (rgb represented as 0 to 1)
const newColor = {r: random[0], g: random[1], b: random[2]};
// Only change the first fill
fills[0].color = newColor;
// Replace the fills on the node.
node.fills = fills;
// }
}
}
// Make sure to close the plugin when you're done. Otherwise the plugin will
// keep running, which shows the cancel button at the bottom of the screen.
figma.closePlugin();
I want my raycaster to look for intersections only when the trigger of my vive controller is pressed.
I thought to initialize the raycaster with a fake class in objects, change it to the real one .prop and gather the intersectedEls
let rightHand = document.getElementById('rightController');
rightHand.setAttribute('line', 'color: purple; opacity: 1;');
rightHand.setAttribute('raycaster', { showLine: true, objects: '.none' });
rightHand.setAttribute('cursor', { downEvents: ['triggerdown'], upEvents: ['triggerup'], rayOrigin: 'entity', fuse: false });
let scene = document.getElementById('scene');
scene.addEventListener('triggerdown', this.myTriggerDown);
scene.addEventListener('triggerup', this.myTriggerUp);
myTriggerDown() {
let rightHand = document.getElementById('rightController');
rightHand.setAttribute('raycaster', { showLine: true, objects: '.prop' });
rightHand.components['raycaster'].refreshObjects();
let raycaster = rightHand.components['raycaster'];
let intersectedEls = raycaster.intersectedEls;
if (typeof intersectedEls !== 'undefined' && intersectedEls.length > 0) {
scene.components['resize'].enableResize(intersectedEls[0]);
} else {
console.log('1234 no intersections')
}
}
myTriggerUp() {
let rightHand = document.getElementById('rightController');
rightHand.setAttribute('raycaster', { showLine: true, objects: '.none' });
}
Unfortunately I keep getting the console.log('1234 no intersections') message.
I tried adding the refreshObjects() line with no effects.
I tried toggling the enabled property instead of changing the objects, but had still the same result.
Any help would be appreciated. Thanks
edit:
if I look for intersections in the triggerup part it works. but this is a workaround which also deprives me of using the intersected element and doing things while keeping the trigger pressed . I'd still like to know why it does not work to enable the ray/change target objects and immediately look for intersections.
I recommend using the raycaster.enabled property versus swapping to a dummy class.
The raycaster checks for intersections once per frame (or whatever the raycaster.interval is). On trigger down, you enable the raycaster, but you have to wait until the next frame for it to pick up intersections.
You can manually invoke intersection check via raycaster.checkIntersections() or run a setTimeout before checking.
I've cobbled together this little sandbox to demonstrate: https://codesandbox.io/s/64xv97y45n
The purple div in the upper left hand corner is supposed to move as letters are typed. When a letter is typed, the currIndex (the currently active box) value on the redux store is incremented or decremented accordingly. The reducer then uses currIndex to compute the currentCoords or the div's new absolute position (for the purposes of attached sandbox, 'slightly more to the right'). The currentCoords store property is then passed on as a prop to control the dynamic pose of the purple div. But the div refuses to update its pose. Redux DevTools tells me currentCoords is updating properly, or at least well enough for it to move a little. What gives?
Relevant logic:
const reducer = (state = initState, action) => {
switch (action.type) {
case "KEYPRESS":
return {
...state,
number: [...state.number, action.key],
currIndex: ++state.currIndex,
currentCoords: state.boxCoords[state.currIndex]
};
<SNIP>
const Cursor = posed.div({
visible: {
opacity: 1,
x: ({ xPos }) => xPos,
y: ({ yPos }) => yPos
},
hidden: {
opacity: 0
}
});
<SNIP>
<Cursor
className="cursor"
initialPose="visible"
xPos={this.props.currentCoords.x}
yPos={this.props.currentCoords.y}
/>
If you want to transition your posed element without changing the current pose you need to pass a poseKey to react on. Also according to documentation of initialPose property:
Once the component mounts, it will transition from this pose into pose.
That is why have must pass pose property to the posed component otherwise initialPose will be reset. So basically <Cursor> component should be rendered like this:
<Cursor
className="cursor"
initialPose="visible"
pose="visible" // you can hold the pose in your state and use it here like this.state.pose
poseKey={this.props.currentCoords.x}
xPos={this.props.currentCoords.x}
yPos={this.props.currentCoords.y}
/>
I have a Qualtrics survey containing a few questions with custom JavaScript (to enable a slider with two slider knobs). I can a) copy the survey as well as b) export the survey as a .qsf file and re-import it. In both cases, I get a new, working survey.
However, importing the survey questions to an existing survey using the "Import Questions From..." function does not work; the JavaScript fails to work in this case. Is there a way to import these questions to an existing survey while preserving the JavaScript?
The code used in the first (most relevant) question:
Qualtrics.SurveyEngine.addOnload(function()
{
document.getElementById("QR~QID7").setAttribute("readonly", "readonly");
document.getElementById("QR~QID8").setAttribute("readonly", "readonly");
var surface;
var cnst = 4;
// Sets the sliders parameters and starting values
$("#research-slider").slider({ id: "research-slider", min: 0, max: 10, range: true, value: [0, 10]});
// variable to store in surface area when user has stopped sliding
var surface, currentResponse;
$("#research-slider").on("slideStop", function(slideEvt) {
surface = slideEvt.value;
document.getElementById("minimum").innerHTML = surface[0];
document.getElementById("maximum").innerHTML = surface[1];
document.getElementById("newValue").innerHTML = (surface[1]- surface[0])/cnst ;
document.getElementById("QR~QID7").value = surface[0];
document.getElementById("QR~QID8").value = surface[1];
});
$('NextButton').onclick = function (event) {
// and now run the event that the normal next button is supposed to do
Qualtrics.SurveyEngine.navClick(event, 'NextButton')
}
})
Your JavaScript won't work when imported because you are using fixed QID codes (QID7 and QID8). The solution is to write your code to find the correct elements by walking the DOM. The easiest way is to use prototypejs instead of native JavaScript.
Assuming the min (QID7) and max (QID8) follow immediately after the question your code is attached to, then it would be something like:
var qid = this.questionId;
var min = $(qid).next('.QuestionOuter').down('.InputText');
var max = $(qid).next('.QuestionOuter',1).down('.InputText');
min.setAttribute("readonly", "readonly");
max.setAttribute("readonly", "readonly");
var surface;
var cnst = 4;
// Sets the sliders parameters and starting values
$("#research-slider").slider({ id: "research-slider", min: 0, max: 10, range: true, value: [0, 10]});
// variable to store in surface area when user has stopped sliding
var surface, currentResponse;
$("#research-slider").on("slideStop", function(slideEvt) {
surface = slideEvt.value;
$("minimum").innerHTML = surface[0];
$("maximum").innerHTML = surface[1];
$("newValue").innerHTML = (surface[1]- surface[0])/cnst ;
min.value = surface[0];
max.value = surface[1];
});