0

I am making a really basic game engine with Nextjs, React hooks and svg.

I want to detect collisions between two svgs, but I'm not sure how to do it.

After trying this syntax

circle.isPointInFill(new DOMPoint(10, 10))

I switched to using createSVGPoint after I looked at this so-question

I want to use React's useRef to get a reference to an svg (and I really do not want to use JQuery), but I cannot seem to get the createSVGPoint function to work so I can use isPointInFill to check if the point is inside an svg.

I saw a similar problem here similar problem in Angular but I don't know how to do similar to this with useRef?

document.createElementNS('http://www.w3.org/2000/svg', 'svg');

Here is my index.js

import Head from 'next/head'
import { useState, useEffect, useRef } from 'react'
import {useKeyPress} from '../hooks/useKeyPress'
import Circle from '../components/svgs/circle'
import Room from '../components/svgs/room'
import utilStyles from '../styles/utils.module.css'

export default function Home() {
  const [ currentXPos, setCurrentXPos ] = useState(500)
  const [ currentYPos, setCurrentYPos ] = useState(500)
  const [ colourIndex, setColourIndex ] = useState(0)

  const circle = useRef()

  const circleColours = [
    "green",
    "orange",
    "red",
    "blue"
  ]

  const upPress = useKeyPress('w');
  const rightPress = useKeyPress('d');
  const leftPress = useKeyPress('a');
  const downPress = useKeyPress('s');

  const setNewXPos = (circle, changeXPos) => {
    let point = circle.createSVGPoint();
    point.x = 40;
    point.y = 32;
    let _newColourIndex = (colourIndex + 1) % 4

    let _newXPos = currentXPos + changeXPos
    if (_newXPos < 50) {
      _newXPos = currentXPos
    } else if (_newXPos > 950) {
      _newXPos = currentXPos
    }

    if (_newXPos % 150 === 0) {
      setColourIndex(_newColourIndex)
    }

    console.log("CHECK IF POINT IN CIRCLE REF", circle.isPointInFill(point))

    setCurrentXPos(_newXPos)
  }

  const setNewYPos = (changeYPos) => {
    let _newColourIndex = (colourIndex + 1) % 4
    let _newYPos = currentYPos + changeYPos
    if (_newYPos < 50) {
      _newYPos = currentYPos
    } else if (_newYPos > 800) {
      _newYPos = currentYPos
    }

    if (_newYPos % 150 === 0) {
      setColourIndex(_newColourIndex)
    }
    
    setCurrentYPos(_newYPos)
  }

  useEffect(() => {
    console.log("CIRCLE", circle.current)
    if (upPress) {
      console.log("UP!")
      setNewYPos(-1)
    }
    if (rightPress) {
      console.log("RIGHT!")
      setNewXPos(circle, 1)
    }
    if (leftPress) {
      console.log("LEFT!")
      setNewXPos(circle, -1)
    }
    if (downPress) {
      console.log("DOWN!")
      setNewYPos(1)
    }
  }, [ 
    currentXPos,
    currentYPos,
    circleColours,
    circle,
    upPress, 
    rightPress, 
    leftPress, 
    downPress
  ])

  return (
    <div className="container">
      <Head>
        <title>Game Engine Thing</title>
      </Head>

      <main>
        <h1 className="title">
         Very Basic React Game Engine {currentXPos} {currentYPos}
        </h1>    

        <Room />

        {currentXPos && <section className={`${utilStyles.gameEnv}`}>
        <svg ref={circle} width="1000" height="1000">
          <Circle
            xPos={currentXPos}
            yPos={currentYPos}
            fill={circleColours[colourIndex]}
          />
        </svg>
        </section>}

      </main>
    </div>
  )
}

Complete code is at complete code

Davtho1983
  • 3,827
  • 8
  • 54
  • 105

1 Answers1

1

So the problem was which objects I was referencing with the function, and to make something that worked nicely I had to change my SVG and change where I put my refs. Below is the new index.js which now allows a circle to move around the edges of an svg path as if the path were the walls of a room.

import Head from 'next/head'
import { useState, useEffect, useRef } from 'react'
import {useKeyPress} from '../hooks/useKeyPress'
import utilStyles from '../styles/utils.module.css'

export default function Home() {
  const [ currentXPos, setCurrentXPos ] = useState(100)
  const [ currentYPos, setCurrentYPos ] = useState(100)
  const [ colourIndex, setColourIndex ] = useState(0)

  const circle = useRef()
  const path = useRef()

  const circleColours = [
    "green",
    "orange",
    "red",
    "blue"
  ]

  const upPress = useKeyPress('w');
  const rightPress = useKeyPress('d');
  const leftPress = useKeyPress('a');
  const downPress = useKeyPress('s');

  const setNewXPos = (changeXPos) => {
    
    let _newColourIndex = (colourIndex + 1) % 4

    let _newXPos = currentXPos + changeXPos

    if (_newXPos % 150 === 0) {
      setColourIndex(_newColourIndex)
    } 

    let point = circle.current.createSVGPoint();
    point.x = _newXPos;
    point.y = currentYPos;

    console.log("CHECK IF POINT IN PATH REF", point, path.current.isPointInFill(point))

    if (path.current.isPointInFill(point)) {
      console.log("GOT HERE!!!!!!")
      setCurrentXPos(_newXPos)
    } else {
      _newXPos = currentXPos
    }
  }

  const setNewYPos = (changeYPos) => {
    let _newColourIndex = (colourIndex + 1) % 4
    let _newYPos = currentYPos + changeYPos


    if (_newYPos % 150 === 0) {
      setColourIndex(_newColourIndex)
    }
    
    let point = circle.current.createSVGPoint();
    point.x = currentXPos;
    point.y = _newYPos;

    console.log("CHECK IF POINT IN PATH REF", point, path.current.isPointInFill(point))

    if (path.current.isPointInFill(point)) {
      setCurrentYPos(_newYPos)
    } else {
      _newYPos = currentYPos
    }

  }

  useEffect(() => {
    console.log("CIRCLE", circle.current)
    if (upPress) {
      console.log("UP!")
      setNewYPos(-1)
    }
    if (rightPress) {
      console.log("RIGHT!")
      setNewXPos(1)
    }
    if (leftPress) {
      console.log("LEFT!")
      setNewXPos(-1)
    }
    if (downPress) {
      console.log("DOWN!")
      setNewYPos(1)
    }
  }, [ 
    currentXPos,
    currentYPos,
    circleColours,
    circle,
    upPress, 
    rightPress, 
    leftPress, 
    downPress
  ])

  return (
    <div className="container">
      <Head>
        <title>Game Engine Thing</title>
      </Head>

      <main>
        <div className={`${utilStyles.mainContainer}`}>
          {currentXPos && <section className={`${utilStyles.gameEnv}`}>
            <svg ref={circle} width="1000" height="800" viewBox="0 0 1000 750" fill="none" xmlns="http://www.w3.org/2000/svg">
              <rect width="1000" height="750" fill="white"/>
              <path ref={path}  fill-rule="evenodd" clip-rule="evenodd" d="M49 51H449V165.239H884V430.332H949V713H214V611.942H172V343.92H49V51Z" fill="#C4C4C4"/>
              <circle  cx={currentXPos} cy={currentYPos} r="40" stroke="green" strokeWidth="4" fill={circleColours[colourIndex]} />
            </svg>
          </section>}
        </div>
        

      </main>
    </div>
  )
}
Davtho1983
  • 3,827
  • 8
  • 54
  • 105