2

I am learning ReactJS by trying to coding a game.
In this game i want that at a specify period of time/frequency a random number between one and four, of enemy (represented by images) appear at the top of the screen and fall toward the bottom of the screen. When they arrived at the bottom of the screen the relevant row disapear. enter image description here

Until now i ve succeeded to get that at each load of a page a random number of ennemies appears at a random offset at the top of the screen and they fall to the bottom of the screen.
The problem is that there is only a unique row that is generated and it does it only one time.

Here my actual code (that works):

class EnemyManager extends React.Component{

    constructor(props) {
        super(props);
        this.state={
            enemyNbr: Math.floor(Math.random() * 5) + 1
        }
    }

    generate_enemy_fct(){
        console.log("enemyNbr:" + this.state.enemyNbr);  // Test--------------------------------------------------------
        let enemyArr = new Array(4);
        if (1 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[0] = <Enemy enemySrcImg="./gameData/nig01.webp"/>;
        }
        if(2 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[1] = <Enemy enemySrcImg="./gameData/nig02.webp"/>;
        }
        if(3 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[2] = <Enemy enemySrcImg="./gameData/nig03.webp"/>;
        }
        if(4 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[3] = <Enemy enemySrcImg="./gameData/nig04.webp"/>;
        }
    return enemyArr;
    }

    render(){
        let enemyArrTmp = this.generate_enemy_fct(true);
        return(
            <div className="EnemyManager">
                {enemyArrTmp[0]}
                {enemyArrTmp[1]}
                {enemyArrTmp[2]}
                {enemyArrTmp[3]}
            </div>
        )
    }
}

And i would like that it would behave like this:

class EnemyManager extends React.Component{

    constructor(props) {
        super(props);
        this.state={
            enemyNbr: Math.floor(Math.random() * 5) + 1
        }
    }

    generate_enemy_fct(){
        console.log("enemyNbr:" + this.state.enemyNbr);  // Test--------------------------------------------------------
        let enemyArr = new Array(4);
        if (1 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[0] = <Enemy enemySrcImg="./gameData/nig01.webp"/>;
        }
        if(2 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[1] = <Enemy enemySrcImg="./gameData/nig02.webp"/>;
        }
        if(3 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[2] = <Enemy enemySrcImg="./gameData/nig03.webp"/>;
        }
        if(4 <= this.state.enemyNbr && this.props.loopEnemyBool === true){
            enemyArr[3] = <Enemy enemySrcImg="./gameData/nig04.webp"/>;
        }
    return enemyArr;
    }

    render(){
        let enemyArrTmp = this.generate_enemy_fct(true);
        return(
            <div className="EnemyManager">
                for(let i=0; i<10**6; i++){
                    if(i%1000===0){ // For exemple generate a new row of enemies at each 1000th iteration
                let enemyArrTmp = this.generate_enemy_fct(true);
                    }
                {enemyArrTmp[0]}
                {enemyArrTmp[1]}
                {enemyArrTmp[2]}
                {enemyArrTmp[3]}
            }

            </div>
        )
    }}

I am really stuck on this problem thank for your help.


EDIT: As it had been asked me here the code of the <Enemy/> component.

class Enemy extends React.Component{
    constructor(props) {
        super(props);
        this.enemyImg = this.props.enemySrcImg;
        this.state={
            posYenemy:0,
            posXenemy:0,
        }
        this.refDivEnemy=React.createRef();
    }


    componentDidMount () {
        this.setState({posXenemy: Math.floor(Math.random() * (window.innerWidth- this.refDivEnemy.current.offsetWidth*1.15))});

        this.timerID = setInterval(() => {
            if(this.state.posYenemy+this.refDivEnemy.current.offsetHeight*1.15<window.innerHeight){
                console.log("posYenemy:" + this.state.posYenemy +"- windowHeight:" + window.innerHeight + "- enemyImgHeight:" + this.refDivEnemy.current.offsetHeight);  // Test--------------------------------------------------------
                this.setState((prevState) => ({
                    posYenemy: Math.floor(prevState.posYenemy + window.innerHeight * 0.05)
                }))
            }
            }, 500);
    }

    componentWillUnmount(){
        clearInterval(this.timerID);
    }

    render(){
        return(
            <div className="enemyDiv"
                 ref={this.refDivEnemy}
                 style={{
                     position: "absolute",
                     top: `${100*this.state.posYenemy/window.innerHeight}vh`,
                     left: `${100*this.state.posXenemy/window.innerWidth}vw`
                 }}>
                <img className="enemyImg"
                     alt="enemy_img"
                     src={require(`${this.enemyImg}`)}
                     style={{
                          height: "10vh"
                      }}
                />
            </div>
        )
    }
}
5
  • 1
    can you share the <Enemy/> component? Commented Aug 30, 2022 at 11:53
  • @HasanAli I ve added it thk for your help Commented Aug 30, 2022 at 12:02
  • 1
    one solution may be to add a condition to the <Enemy/> component, when its position passes a certain point, give it a new random position at the top of the page. Commented Aug 30, 2022 at 12:06
  • I ve tried it but it just generate the same row again at the top of the screen, or/and just one row appears. But i will try it again asap. Commented Aug 30, 2022 at 12:08
  • 1
    I see the problem now. I don't know how how to fix it. Commented Aug 30, 2022 at 12:17

1 Answer 1

1

The problem is, your Enemy Manager class isn't doing enough managing. As of now, it does not know where its children (Enemy) are on the screen, so it can't make a decision on whether or not to render them. In addition to creating new enemies, your Enemy Manager class needs to decide where each Enemy is on the screen, and determine if it should continue to exist.

Here's what you can do to fix this:

  1. Create a EnemyProps object, something like:
{image: 'foo.webp', xPos: 0, yPos: 0}
  1. Remove the interval from the Enemy component. You don't need state there anymore either. Position will now be passed into the component via props.
  2. Store an array of EnemyProps in the Enemy Manager's state. It should start off empty.
  3. On the Enemy Manager, on Component Did Mount, set an interval. On each iteration:
  • Clone the EnemyProps array
  • Update the Y position of each object in the array. If the object's new Y position indicates that it is at the bottom, remove it from the cloned array.
  • If this is the 1000th iteration (or whatever you decide), call the generate_enemy function, which you will modify to create EnemyProps objects instead of <Enemy> components. The function will assign an image and a random X position to each object it creates, then push it onto the cloned array.
  • Set state to use the cloned EnemyProps array.
  1. Your Render function should do this: state.enemies.map(enemy => <Enemy props={enemy} />
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.