bberak / react-native-game-engine

A lightweight Game Engine for React Native 🕹⚡🎮
MIT License
2.89k stars 174 forks source link

Nested entity property change doesn't trigger rerender process #65

Closed AlekseiAfanasev closed 2 years ago

AlekseiAfanasev commented 3 years ago

Hi. I'm using react-native-game-engine with Matter-js for my project. I faced with an issue of how render() is triggered. I got entity like this:

const Entity = (
  physicsBody: Matter.Body,
  size: SizeComponent,
  color: ColorComponent
): EntityPropsType => {
  return { physicsBody, size, color };
};

And just simple renderer:

class EntityRenderer extends PureComponent<EntityPropsType> {
  render() {
    const x = this.props.physicsBody.position.x - this.props.size.width / 2;
    const y = this.props.physicsBody.position.y - this.props.size.height / 2;
    return (
      <View
        style={[
          styles.finger,
          {
            left: x,
            top: y,
            borderRadius: this.props.size.width,
            width: this.props.size.width,
            height: this.props.size.height,
          },
        ]}
      />
    );
  }
}

It is registered and works. But in my MovementSystem when I just do

entity.physicsBody.position.x = ...

it is not being rerendered. Currently I fix this just by reassigning (and recreating, actually) color component, as it is flat.

Is it a bug or did I miss something in docs? As I see here (https://github.com/bberak/react-native-game-engine/blob/master/src/GameEngine.js#L116) it is desired behaviour. But is it valid?

AlekseiAfanasev commented 3 years ago

I guess this is not about nested structures, but about Objects itself - when I modify physicsBody, it is not actually new object, so updateHandler does not see any changes and doesn't setState

I know I can do

entity.physicsBody = {...entity.physicsBody}

but is it the only solution?

bberak commented 3 years ago

Hey @AlekseiAfanasev,

I think a few others have faced this similar issue when using RNGE with MatterJS. As you pointed out, your entity is not being re-rendered because PureComponent will only do a shallow comparison between the current props and the next props - and seeing as they are the same object - it won't detect a change and hence skip the update.

Do the x and y properties of your entity change often? If so, then maybe switching from PureComponent to Component might be an option? Or using functional components

The other option (which probably isn't ideal) is to copy the x and y values into separate properties:

entity.physicsBody.position.x = newX
entity.physicsBody.position.y = newY
entity.x = newX
entity.y = newY

Let me know how that goes 🙏

AlekseiAfanasev commented 3 years ago

Thank you for quick response! Well, I totally forgot that I'm using PureComponent there instead of Component :-) Will switch