I create an array of cylinder meshes and render them in a loop; this works fine… but whenver I change a position of another mesh (Player, has nothing to do with this component) it causes the whole scene to rerender which causes a rerender of my Coins component (since there is a random() function for selecting the texture of each coin, coins start to get random textures everytime something in the scene changes)
import React from "react";
import { useFrame } from "@react-three/fiber";
import { useRef, useState, useEffect } from "react";
import { Color, RepeatWrapping} from "three";
import {useTexture} from '@react-three/drei'
import { state as globalState } from "./State";
import { useSnapshot } from "valtio";
export const Coins = React.memo(function Coins() {
const snap = useSnapshot(globalState)
const [obstacles, setObstacles] = useState([{}]);
const obstaclesRef = useRef([]);
const initialObstaclesRef = useRef([]);
const zPosition = -30;
let distanceSum = 0;
const logos = ["euro.png", "beefy.png", "dollar.png", "yen.png"]
const textures = useTexture(logos.map(logo => `logos/img/${logo}`));
useEffect(() => {
// console.log(snap.bullPosition)
for(let i=0;i<20;i++){
const randomTextureIndex = Math.floor(Math.random() * textures.length); // Get a random texture index
distanceSum += Math.random() * (4 - 2) + 2;
initialObstaclesRef.current.push({
id: i,
randX: Math.floor(Math.random() * 3) - 1, // Random X position: -1, 0, 1
z: zPosition-distanceSum,
texture: textures[randomTextureIndex]
})
}
setObstacles(initialObstaclesRef.current);
}, [textures])
const playCoinSound = () => {
if(snap.fxMuted) return;
const audio = new Audio('coin.mp3');
audio.play().catch(error => console.error("Audio play failed", error));
};
useFrame((state, delta) => {
obstaclesRef.current.forEach((mesh, i) => {
if (mesh) {
mesh.position.z += delta*5*snap.speed;
if(mesh.position.z > 5){
const randomTextureIndex = Math.floor(Math.random() * textures.length); // Get a random texture index
const myTexture = textures[randomTextureIndex];
myTexture.wrapS = RepeatWrapping;
myTexture.wrapT = RepeatWrapping;
myTexture.repeat.set(1, 1);
mesh.material.map = myTexture;
mesh.position.z = zPosition*2;
mesh.position.x = Math.floor(Math.random() * 3) - 1;
}
if(mesh.position.z >0){ // coin cube hit touches beefy
if(mesh.position.x === snap.bullPosition[0]){
playCoinSound();
mesh.rotation.y +=0.3;
globalState.count +=1;
globalState.isHit = true;
}else{
globalState.isHit = false;
}
}
}
});
});
const texture = useTexture('Moon_texture.jpg');
// emissive={new Color(0.4, 1, 0.4)}
return (
<>
{obstacles.map((obstacle, i) => (
<mesh
key={obstacle.id}
position={[obstacle.randX, -1, obstacle.z]}
castShadow
receiveShadow
rotation={[1.57, 0, 0]}
ref={(el) => (obstaclesRef.current[i] = el)}
>
<cylinderGeometry args={[0.5, 0.5, 0.1]} />
<meshStandardMaterial map={textures[Math.floor(Math.random() * textures.length)]} color={0xffffff} metalness={1} roughness={0.5} />
</mesh>
))}
</>
);
});
in the canvas:
<canvas>
<Player/>
<Coins />
</canvas>
in PlayerI have:
useEffect(() => {
const unsubscribe = subscribeKey(state, 'directionVal', (directionVal) => {
const playerMesh = group.current;
if (!playerMesh) return;
if (directionVal > 0) {
playerMesh.position.x += 0.1;
} else if (directionVal < 0) {
playerMesh.position.x -= 0.1;
}
});
return () => unsubscribe();
}, []);