简体   繁体   中英

Create a loop on react JSX component

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. 在此处输入图像描述

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>
        )
    }
}

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} />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM