A Web+Android+iOS game boilerplate in expo react native
Inherit all yarn commands from expo
Inspired by react-native-game-engine
yarn # install the dependencies
expo start # start the expo development server
# then you can start android, ios, or web whatever you like
expo build:web # static files at web-build
import React, {useMemo, useState} from 'react'
import {Engine} from './Engine'
import {Vertex} from './Vertex'
() => {
const [running, setRunning] = useState(true)
const entities = useMemo(() => ({
1: {x: 50, y: 50, style: {backgroundColor: 'black'}, Component: Vertext},
2: {x: 200, y: 200, style:{width: 100, height: 100, backgroundColor: 'pink'}, Component: Container}
/* other entities */
}), [])
return <Engine entities={entities} systems={[move, /* other systems */]} running={running}/>
}
The stage that all entities are in and interactions happened
An object that maps entity id to entity definition, which consist of x, y, Component, and other props that will pass to the component.
Component need to have Container as root or wrapped by makeInteractive
import Container, { ContainerProps, ContainerPropTypes } from '../../Engine/Container'
import React, { FunctionComponent } from 'react'
import PropTypes from 'prop-types'
const Vertex: FunctionComponent<ContainerProps> = ({ style, ...props }) => {
return (
<Container
{...props}
style={{
width: 16,
height: 16,
backgroundColor: 'pink',
...style
}}
/>)
}
Vertex.propTypes = {
...ContainerPropTypes,
style: PropTypes.object
}
export default Vertex
import React, { FunctionComponent } from 'react'
import { View } from 'react-native'
import {
ElementProps,
elementPropTypes,
InteractiveProps,
interactivePropTypes,
makeInteractive
} from './makeInteractive'
import PropTypes from 'prop-types'
const propTypes = {
...elementPropTypes,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
style: PropTypes.object
}
const _Container: FunctionComponent<ElementProps & PropTypes.InferProps<typeof propTypes>> = ({ x = 0, y = 0, style = {}, ...props }) => (
<View
style={{
position: 'absolute',
left: x,
top: y,
...style
}}
{...props}
/>
)
_Container.propTypes = propTypes
export const ContainerPropTypes = {
...interactivePropTypes,
x: PropTypes.number,
y: PropTypes.number,
style: PropTypes.object
}
export type ContainerProps = PropTypes.InferProps<typeof ContainerPropTypes> & InteractiveProps & ElementProps
const Container: FunctionComponent<ContainerProps> = makeInteractive(_Container)
Container.propTypes = ContainerPropTypes
export default Container
A system is a function that is called automatically for every animation frame
(the period is system determined, so it is dynamic),
and called in the sequence that it is passed to the engine.
A system will accept entities from the previous frame or entities returned by previous system as first parameter,
and second parameter is the object {events, currentMs, deltaMs}
, and it should return new entities.
import { System } from '../../Engine'
export const move: System = (entities, { events }) => {
const movingEntities = events.filter(({ type }) => type === 'move')
movingEntities.forEach((event) => {
const entity = entities[event.id]
if (event.startPositions !== undefined) {
entity.x = event.positions.pageX - event.startPositions.locationX
entity.y = event.positions.pageY - event.startPositions.locationY
}
})
return entities
}
A list of event is passed to each of the systems
interface InteractEvent {
id: string | number // entity id
type: InteractType // 'down' | 'move' | 'up'
event: SyntheticEvent // the native event
positions: Positions, // global and local position
startPositions?: Positions // global and local position of previous down event
startTime?: number // time that previous down event occur
}
interface Positions {
// global position
pageX: number
pageY: number
// local position
locationX: number
locationY: number
}
down
is the event of mouse down or touch start
up
is the event of mouse up or touch end
move
is the event of mouse move while mouse down or touch move