bberak / react-native-game-engine

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

Does it make sense to use the GameEngine between other components like View #46

Open ihsanktmr opened 4 years ago

ihsanktmr commented 4 years ago

When I am trying to add </GameEngine> between other </View> components however its sensibility decreases a lot and also it definitely becomes unable to use. When I define objects for to appear at the beginning of render. They scattering out and sort of teleporting through other components they even can pass through other objects like they never stops.

Below is how I am trying to use the <GameEngine> component

I want to ask that if I should only use <GameEngine> as the most upper JSX component ?

Thanks.

import React from 'react'
import { View, TouchableOpacity, Dimensions
} from 'react-native'
import _ from 'lodash'
import AmountWithCurrency from "../../text/AmountWithCurrency"
import {GameEngine} from "react-native-game-engine"
import {Physics, CreateBox, MoveBox, CleanBoxes} from "./systems"
import {Box} from "./renderers";
import Matter from 'matter-js'
import {Ball2} from "./Ball2"

const isAction = false;

class Balls extends React.Component {
    state = {}

    render() {
        const {currency, data, totalPrice, isAmount, type} = this.props
        //const { coordinate, showTooltip, name, price } = this.state
        const sortedData = _.reverse(_.sortBy(data, ['price']))

        const {width, height} = Dimensions.get("window")
        const engine = Matter.Engine.create({enableSleeping: false})
        const world = engine.world
        const ball1 = Matter.Bodies.circle(60, 80, 25,
            {frictionAir: 0.021})      //first two parameters are for starting position of balls or any other objects
        const ball2 = Matter.Bodies.circle(80, 80, 25,
            {frictionAir: 0.021})
        const ball3 = Matter.Bodies.circle(55, 50, 25,
            {frictionAir: 0.021})
        const floor = Matter.Bodies.rectangle(
            width / 2,  //x
            height + 90, //y
            width, //width
            10, //height
            {isStatic: true}) //options
        const constraint = Matter.Constraint.create({
            label: "Drag Constraint",
            pointA: {x: width, y: height},
            pointB: {x: width, y: height},
            length: 1,
            stiffness: 1,
            angularStiffness: 0,
        })

        Matter.World.add(world, [ball1, floor])
        Matter.World.add(world, [ball2])
        Matter.World.add(world, [ball3])
        Matter.World.addConstraint(world, constraint)

        return (
            <TouchableOpacity
                activeOpacity={1}
                style={{
                    marginHorizontal: -16,
                    marginTop: 15,
                    flexDirection: 'column',
                    justifyContent: 'center',
                    backgroundColor: 'green'
                }}
            >
                <View style={{flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 35}}>
                    {!_.isEmpty(sortedData) &&
                    sortedData.map((item, index) =
                        return index < 3 ? (
                            <View
                                key={index}
                                style={{
                                    flexDirection: 'row',
                                    marginBottom: 20
                                }}
                            >
                                <View
                                    style={{
                                        height: 8,
                                        width: 8,
                                        backgroundColor: color,
                                        borderRadius: 8,
                                        marginTop: 5
                                    }}
                                />
                                <View style={{flexDirection: 'column', marginLeft: 12}}>
                                    <Text style={{color: '#2E3D5C', fontWeight: '600'}}>
                                        {item.name}
                                    </Text>
                                    <Text
                                        style={{
                                            marginTop: 5,
                                            color: 'rgba(46, 61, 92, 0.4)'
                                        }}
                                    >
                                        {(100 * (item.price / totalPrice) || 0).toFixed(1) + ' %'}
                                    </Text>
                                </View>
                            </View>
                        ) : null
                    })}
                </View>
               <GameEngine
                        style={{width: width, height: 500,backgroundColor:'brown'}}
                        systems={[Physics, CreateBox, MoveBox, CleanBoxes]}
                        entities={{
                            physics: {engine: engine, world: world, constraint: constraint},
                            ball1: {body: ball1, size: [50, 50], color: "green", renderer: Ball2},
                            ball2: {body: ball2, size: [50, 50], color: "blue", renderer: Ball2},
                            ball3: {body: ball3, size: [50, 50], color: "yellow", renderer: Ball2},
                            floor: {body: floor, size: [width, 10], color: "#86E9BE", renderer: Box},
                        }}
                    >
                    </GameEngine>
                <View
                    style={{
                        justifyContent: 'center',
                        alignItems: 'center',
                        marginTop: 20
                    }}
                >
                    <AmountWithCurrency
                        currency={currency}
                        currencyColor={'#8992A3'}
                        currencySize={14}
                        amountTextStyle={{fontSize: 15, fontWeight: '600'}}
                        amount={totalPrice}
                        width={180}
                    />
                    <Text style={{color: '#8992A3', fontSize: 11, marginTop: 4}}>
                        total
                    </Text>
                </View>
            </TouchableOpacity>
        )
    }
}

export default Balls
bberak commented 4 years ago

Hi @ihsanktmr,

You can definitely use the between and amongst other components. We did some updates to this functionality recently: https://github.com/bberak/react-native-game-engine/issues/38

That said, I would first make sure your game works well as the root, fullscreen component and then slowly add sibling components as required.

Also, the GameEngine accepts a style prop that might help you with your Layout issues. In terms of scattering - that sounds like a MatterJS physics problem. Does the scattering happen on the first load? Also I couldn't see your Ball renderer - are you setting the position to absolute?

It also looks like you are creating your entities in the render() { method. Could this method be called many times during load - and if so, that will definitely hurt performance because you'll be generating your entities and physics world over and over. Put a few console.log("!!"); calls around your components to find out if they're being re-created (there's probably many better ways to debug this - but console.log is nice and quick).

ihsanktmr commented 4 years ago

I removed <View> nothing changed. Moreover it started behave more stranger. When I create balls, they just slide over ground. I mean even though I don't drag them, they go under the floor by themselves.

I've checked console.log too. Appearently there is no a render problem. Setting position 'absolute' does not make difference too it just made it worse.

And I am not able see balls which should have be created at the beginning and should have fall to the ground. Those 3 balls are invisible I do not know why.

This is my Ball renderer it's just display component here it is;

import React, {Component} from 'react'
import { View } from 'react-native'

class Ball2 extends Component{
    constructor(props) {
        super(props);
    }
    render() {
        const width = this.props.size[0];
        const height = this.props.size[1];
        const x = this.props.body.position.x - width / 2;
        const y = this.props.body.position.y - height / 2;
        const angle = this.props.body.angle;

        return (
            <View
                style={
                    {
                        left: x,
                        top: y,
                        width: width,
                        height: height,
                        borderRadius:25,
                        backgroundColor: this.props.color
                    }
                }
            />
        );
    }
}

export {Ball2};

This is how it looks like; Screen Shot 2020-01-30 at 11 41 32

ihsanktmr commented 4 years ago

Plus, I am not able set any borders to the Matter.World I am trying to do it for like 1 week but couldn't find any solution. Maybe If I could set border or like constraint maybe the GameEngine container would be fixed. However all the examples about it are for web development or HTML5 based, I can not convert it to implement in React Native :/

bberak commented 4 years ago

Sounds like there is a lot happening here @ihsanktmr

I'm not too familiar with MatterJS, but have you had a chance to look at some of the examples here (especially the rigid-bodies example): https://github.com/bberak/react-native-game-engine-handbook/blob/master/app/physics/rigid-bodies/

They're quite old, and probably not coded well, but should work. Perhaps you can try to copy the rigid-bodies example and slowly modify it line by line to suit your needs - making sure it works at every step of the way.

Also, don't be too quick to dismiss position: "absolute" property - because the physics engine will tell you where and how your entity should be placed, it doesn't care about React Native's default flex layout behaviours. If you have a look at the Box renderer inside the rigid-bodies example you'll see that is using position: "absolute".

ihsanktmr commented 4 years ago

Yes thats actually how i made the whole progress exacly I started to doing it by modifying rigid-bodies example.

position: "absolute" does not make big difference but i'll keep it in my mind thanks.

I am not really familiar with matter.js too but it seems like it's the most convenient way to create objects and to use them in <GameEngine/>. So I assumed they're both working better when they're together. As you said this issue probably more related to matter.js, I don't know why but I have a feeling that if I open an issue there in matter.js I will not get an answer because I am trying to do it in React Native and by using react-native game-engine.

In addition, so is it possible to add gravity to objects without using matter.js ? Is matter.js the only way to handle physics ? Thanks

bberak commented 4 years ago

Hi @ihsanktmr,

At what point did your progress deviatate from the intented requirements? Are you able to go to a spot or commit where it was working then slowly go from there?

Matter.js is good if you want to add more complicated physics and collisions to your project, but you can certainly roll your own simple physics system (with gravity, wind, springs etc). I often do this for simple use-cases where something like Matter.js would be overkill.

Here is a simple physics system you can integrate into your own project: https://github.com/bberak/react-native-game-engine-template/blob/master/src/game/systems/basic-physics.js

I can probably give you a simple usage example if required - but if you're comfortable with Matter.js it might not be worth your time..