I use "phaser", 'ion-phaser' and a phaser plugin 'grid-engine' for integrate a phaser game to react.
I can initialize the game successfully, then destroy it without an error. But when I try to reinitialize it İt give 2 errors
Scene Plugin key in use: GridEngine
and
Uncaught TypeError: Cannot read properties of undefined (reading 'create')
at Scene.create (isometric.js:93:1)
at SceneManager.create (phaser.js:84684:1)
at SceneManager.loadComplete (phaser.js:84615:1)
at LoaderPlugin.emit (phaser.js:1671:1)
at LoaderPlugin.loadComplete (phaser.js:165337:1)
at LoaderPlugin.fileProcessComplete (phaser.js:165311:1)
at SpriteSheetFile.onProcessComplete (phaser.js:4290:1)
at data.onload (phaser.js:16548:1)
second error at this line
this.gridEngine.create(cloudCityTilemap, gridEngineConfig);
Additionally when I try a phaser game without plugins like grid engine, there is not an error.
I search and I think I must remove grid-engine plugin when destroy game but I could not find how.
My question is
How to destroy a phaser game with included plugins and restart it?
this is code sample
const game = {
width: 800,
height: 600,
type: Phaser.AUTO,
scene: {
preload,
create,
update,
},
plugins: {
scene: [
{
key: 'GridEngine',
plugin: GridEngine,
mapping: 'gridEngine'
},
],
},
}
const gameRef = useRef(null)
const [initialize, setInitialize] = useState(false)
const destroy = () => {
if (gameRef.current) {
gameRef.current.destroy()
}
setInitialize(false)
}
return (
<>
<IonPhaser game={game} ref={gameRef} initialize={initialize} />
<button onClick={()=>setInitialize(true)}>Initialize</button>
<button onClick={destroy}>Destroy</button>
</>
)
}
Finally I want to change different games or levels at the same page
Im not 100% sure, if this will solve all your problems, but
<button onClick={setInitialize(true)}>
is not correct, you should not call the function, you should only pass it.
Try replacing that line with
<button onClick={ () => setInitialize(true)}>
For Details, checkout this article / documentation for details.
Update: How to get the game object
to get the Phaser Game object you would have to use getInstance (link to ion-phaser documentation) and to remove the Plugin you would have to do something like this:
this.gameRef.current.getInstance()
.then(game => game.plugins.removeScenePlugin("GridEngine"))
Related
General :
TL;DR: async code hangs rendering.
I have this component with a Modal and inside the Modal it renders a list of filters the user can choose from. When pressing a filter the color of the item changes and it adds a simple code(Number) to an array. The problem is that the rendering of the color change hangs until the logic that adds the code to the array finishes.
I don't understand why adding a number to an array takes between a sec and two.
I don't understand why the rendering hangs until the entire logic behind is done.
Notes: I come from a Vue background and this is the first project where I'm using react/react-native. So if I'm doing something wrong it would be much appreciated if someone points that out
Snack that replicates the issue :
Snack Link
My code for reference :
I use react-native with expo managed and I use some native-base components for the UI.
I can't share the whole code source but here are the pieces of logic that contribute to the problem :
Parent : FilterModal.js
The rendering part :
...
<Modal
// style={styles.container}
visible={modalVisible}
animationType="slide"
transparent={false}
onRequestClose={() => {
this.setModalVisible(!modalVisible);
}}
>
<Center>
<Pressable
onPress={() => this.setModalVisible(!modalVisible)}
>
<Icon size="8" as={MaterialCommunityIcons} name="window-close" color="danger.500" />
</Pressable>
</Center>
// I use sectionList because the list of filters is big and takes time to render on the screen
<SectionList
style={styles.container}
sections={[
{ title: "job types", data: job_types },
{ title: "job experience", data: job_experience },
{ title: "education", data: job_formation },
{ title: "sector", data: job_secteur }
]}
keyExtractor={(item) => item.id}
renderItem={({ item, section }) => <BaseBadge
key={item.id}
pressed={this.isPressed(section.title, item.id)}
item={item.name}
code={item.id}
type={section.title}
add={this.addToFilters.bind(this)}
></BaseBadge>}
renderSectionHeader={({ section: { title } }) => (
<Heading color="darkBlue.400">{title}</Heading>
)}
/>
</Modal>
...
The logic part :
...
async addToFilters(type, code) {
switch (type) {
case "job types":
this.addToTypesSelection(code);
break;
case "job experience":
this.addToExperienceSelection(code);
break;
case "formation":
this.addToFormationSelection(code);
break;
case "sector":
this.addToSectorSelection(code);
break;
default:
//TODO
break;
}
}
...
// the add to selection methods look something like this :
async addToTypesSelection(code) {
if (this.state.jobTypesSelection.includes(code)) {
this.setState({ jobTypesSelection: this.state.jobTypesSelection.filter((item) => item != code) })
}
else {
this.setState({ jobTypesSelection: [...this.state.jobTypesSelection, code] })
}
}
...
Child :
The rendering Part
render() {
const { pressed } = this.state;
return (
< Pressable
// This is the source of the problem and read further to know why I used the setTimeout
onPress={async () => {
this.setState({ pressed: !this.state.pressed });
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
<Badge
bg={pressed ? "primary.300" : "coolGray.200"}
rounded="md"
>
<Text fontSize="md">
{this.props.item}
</Text>
</Badge>
</Pressable >
);
};
Expected outcome :
The setState({pressed:!this.state.pressed}) finishes the rendering of the item happens instantly, the rest of the code happens after and doesn't hang the rendering.
The change in the parent state using the add code to array can happen in the background but I need the filter item ui to change instantly.
Things I tried :
Async methods
I tried making the methods async and not await them so they can happen asynchronously. that didn't change anything and seems like react native ignores that the methods are async. It hangs until everything is done all the way to the method changing the parent state.
Implementing "event emit-listen logic"
This is the first app where I chose to use react/react-native, coming from Vue I got the idea of emitting an event from the child and listening to it on the parent and execute the logic that adds the code to the array.
This didn't change anything, I used eventemitter3 and react-native-event-listeners
Using Timeout
This is the last desperate thing I tried which made the app useable for now until I figure out what am I doing wrong.
basically I add a Timeout after I change the state of the filter component like so :
...
< Pressable
onPress={async () => {
// change the state this changes the color of the item ↓
this.setState({ pressed: !this.state.pressed });
// this is the desperate code to make the logic not hang the rendering ↓
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
...
Thanks for reading, helpful answers and links to the docs and other articles that can help me understand better are much appreciated.
Again I'm new to react/react-native so please if there is some concept I'm not understanding right point me in the right direction.
For anyone reading this I finally figured out what was the problem and was able to solve the issue for me.
The reason the rendering was getting hang is because the code that pushes to my array took time regardless of me making it async or not it was being executed on the main thread AND that change was triggering screen re-render which needed to wait for the js logic to finish.
The things that contribute to the solution and improvement are :
Make the array (now a map{}) that holds the selected filters stateless, in other words don't use useState to declare the array, instead use good old js which will not trigger any screen re-render. When the user applies the filters then push that plain js object to a state or context like I'm doing and consume it, doing it this way makes sure that the user can spam selecting and deselecting the filters without hanging the interactions.
first thing which is just a better way of doing what I needed is to make the array a map, this doesn't solve the rerender issue.
Does anybody know if there is any built-in React Native component that render lists like this?
It shouldn't be super hard to implement this from scratch
If you want the exactly same design there are probably three or so components that you need to write
Heres pseudo code
const settings = [
{
items : [{
icon : SomeIcon,
label : "Notifications",
action: () => ...navigate somewhere
},
...more items
]
}
]
...more code
return
<FlatList>
{settings.map(setting =>{
return <SettingSection>
{setting.items.map(item =>{
return <Item/>
})
}
</SettingSection>
})}
</FlatList>
You can try to do it by yourself but if you want exactly the same view, here i found a npm package for you. https://www.npmjs.com/package/react-native-settings-list . it is efficient and customizable.
I'm currently working at a project with react-three-fiber. I've imported the model with useGLTF from #react-three/drei.
With
const { nodes, materials } = useGLTF('/model.glb');
I access the materials from the glb-file.
To access and manipulate the model I used gltfjsx to generate the model.
Now I need to change the material of a mesh programmatically. Because I have no direct access to the JSX of the model I do it with React.cloneElement and modify the props of the mesh.
So I tried something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial ?
<meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> :
materials['mat_7']
});
If overwriteMaterial is false it works. It shows the material it should. But if it's true then the mesh disappears.
I also thought of putting the <meshStandardMaterial /> in the children prop of the mesh. Something like so:
return React.cloneElement(mesh, {
material: overwriteMaterial ? undefined : materials['mat_7'],
children: overwriteMaterial ? <meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> : undefined
});
With this I always get this error and I don't know why it appears:
TypeError: Cannot read properties of undefined (reading 'visible')
Could this approach somehow work or am I doing something completely wrong?
Every help is welcome. Thanks
Alright so I found the answer by myself after some more hours searching for a solution.
The material property doesn't accept a JSX-tag. So if you create an instance of the class MeshStandardMaterial you can pass it to the property and it works perfectly fine. Now it looks something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial
? new MeshStandardMaterial({ color: 0x0ff000 })
: materials['mat_7']
})
Note: The class MeshStandardMaterial is exported from the three package.
i really don't think you have to clone any react element, that doesn't seem correct. you can clone or mutate materials just as you would in a plain three app. i have no idea why you even want to clone jsx.
const { scene } = useGLTF(url)
const clonedScene = useMemo(() => scene.clone(), [])
useLayoutEffect(() => {
clonedScene.traverse(o => {
if (o.type === 'Mesh') {
o.material = ...
}
})
}, [clonedScene]}
return <primitive object={clonedScene} />
you can skip the clonedScene thing completely as well, this is only if you plan to re-use the model in your scene multiple times.
takePicture = async function() {
if (this.camera) {
const options = { quality: 0.5, base64: true, };
const data = await this.camera.takePictureAsync(options);
this.setState({path: data.uri})
}
}
As soon as I call my takePicture fuction to capture image, the camera still keeps moving and doesn't pause. I want the camera to pause and then show me the image.
Is there something here to with handling Promise? If yes, I don't know where to do it and how.
I've also tried using pauseAfterCapture:true but it still takes 1 or 2 second to capture the image.
I know this is an old issue but no solution could help me yet. Please help.
I've also found the available camera components very slow, that is why I created react-native-fast-camera and I just open sourced it for the public use. It is very customizable and completely controllable by react native components.
Note: Currently the Android version is still work in progress.
Here is an example:
import FastCamera, { Methods } from 'react-native-fast-camera';
<FastCamera style={{ height: cameraHeight, width: cameraWidth }}
onSaveSuccess={imageUrl => {
console.log("onSaveSuccess: ", imageUrl);
}}
onGalleryImage={imageUrl => {
console.log("onGalleryImage: ", imageUrl);
}}
onFlashToggle={isflashOn => {
console.log('flash info: ', isflashOn);
}}
>
{/* here render your buttons to control the camera component */}
<Button
title="capture picture"
onPress={()=> Methods.takePicture();}
/>
</FastCamera>
And here is a screenshot:
I'm trying to build a simple object-viewer in React with Meteor that can import .obj and .mtl Files using the following npm modules:
three(0.87.1)
react(15.6.1)
three-obj-loader(1.1.3)
three-mtl-loader(1.0.1)
So far i have managed to display an object using the OBJLoader.
But when i try to render an object after applying a texture with MTLLoader, i get this error from console:
Uncaught TypeError: Cannot read property 'toString' of undefined
at WebGLPrograms.getProgramCode (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:50707)
at initMaterial (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54628)
at setProgram (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54820)
at WebGLRenderer.renderBufferDirect (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:53883)
at renderObject (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54613)
at renderObjects (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54586)
at WebGLRenderer.render (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54350)
at WebGlDisplay.renderScene (WebGlDisplay.jsx:86)
at onClick (WebGlDisplay.jsx:90)
at HTMLUnknownElement.boundFunc (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:8794)
Cause: material.onBeforeCompile in getProgramCode is undefined
My code looks like this:
import React, { Component } from 'react'
import THREE from 'three'
const MTLLoader = require('three-mtl-loader');
const OBJLoader = require('three-obj-loader')(THREE);
export default class WebGlDisplay extends Component {
constructor(props) {
super(props)
}
//init canvas
init(){
const width = this.mount.clientWidth;
const height = this.mount.clientHeight;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setClearColor('#000000', 0.2);
renderer.setSize(width, height);
camera.position.set(3,4,6);
camera.lookAt(new THREE.Vector3());
this.scene = scene;
this.camera = camera;
this.renderer = renderer;
this.mount.appendChild(this.renderer.domElement);
}
//load & render object
drawOBJ(){
const mtlLoader = new MTLLoader();
let onProgress = function(e){console.log("rendering:" + e)};
let onError = function(e){console.log("error:" + e)};
mtlLoader.load("eagle.mtl", materials => {
materials.preload();
// OBJ Loader
const objLoader = new THREE.OBJLoader();
this.materials = materials;
objLoader.setMaterials(materials);
objLoader.load("eagle.obj", object => {
this.object = object;
this.scene.add(object);
}, onProgress, onError);
}, onProgress,onError);
this.renderScene();
}
componentDidMount() {
this.init();
this.drawOBJ();
}
renderScene() {
this.renderer.render(this.scene, this.camera)
}
render() {
return (
<div onClick={(e) => this.renderScene()}
style={{ width: '800px', height: '600px' }}
ref={(mount) => { this.mount = mount }}
/>
)
}
}
Does anyone have an idea why i get this error?
I've tried to use different .obj- and .mtl-files, but the error remains (whenever i try to call renderScene()).
By any chance, could it be a problem with the module versions, or maybe some timing problems while loading?
Any help would be appreciated.
The problem seems to be that the three-mtl-loader NPM package is referencing an outdated three.js version in it's package.json, so even though you are using the latest version of three, the plugin isn't!
Obviously this isn't a viable long-term fix, but I changed the version for three in node_modules/three-mtl-loader/package.json to 0.87.1 and deleted the directory node_modules/three-mtl-loader/node_modules for good measure and ran my example and it worked straight away, textures and all.
Clearly the plugin needs to be updated. I also saw at least one functional difference between the source in the plugin and in the three examples folder ('Tr' vs 'tr' in a case statement), and it doesn't follow the same initialization behavior as other loader plugins (specifically it isn't initialized by calling require("three-mtl-loader")(THREE)), so there's a bit of work to get it ship-shape.
Alternately, it appears that the author has updated the version number to 0.86.0 in their repo (which is high enough), just hasn't done a deploy to NPM. So, if you feel brave, you can just change your package.json to have the line
"dependencies": {
...
"three-mtl-loader": "git+https://git#github.com/nascherman/three-mtl-loader.git",
...
}
As a workaround, what i ended up doing is to get a local copy of the newest MTLLoader Version and slightly modifing it, as it seems to be a problem with the version pointed out by #user1691694.
In case somebody needs this here my way of importing it:
In the MTLLoader file, add the following line at the top:
import THREE from 'three';
and at the bottom:
module.exports.default = THREE.MTLLoader
Use it like in the drawOBJ function in the question post and import it in the target file like this:
import MTLLoader from './MTLLoader.js';