13 Comments

Whenever I publish it, you could write the docs for it...

Expand full comment
author

Feel free to reach out when it's published!

Expand full comment
May 6Liked by JSLegendDev

Sure...am currently fixing a bug in the collision system...where if a enemy with a Mass/body component is standing on the ground and I jump on their head then try to move horizontally...the player get thrown back...I think that's where am going to have to create the collision ignore like kaboom

Expand full comment

I just realized something...no game allow you to walk on your enemy head...once collide with the head ,there should be a event handler that either destroy the player or the enemy...while applying jump force 🤔

Expand full comment

React native skia is great... this my render...const Render = () => {

const { SCREEN_HEIGHT, SCREEN_WIDTH ,MAP_OFFSET} = useConstants();

const bg =useImage(require("../../assets/maps/world_1.png"))

const systems = useRegistry((state) => state.systems);

if (!systems[SystemType.RENDER]) {

return null;

}

return (

<Canvas style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT }}>

<Image

x={0}

y={MAP_OFFSET}

//transform={[{scale:1.7}]}

width={211 * 16}

height={15 * 16}

image={bg}

/>

{

systems[SystemType.RENDER].entities.map((entity, index) => {

const pos = entity.components.find(

(c) => c.name === CompName.POSITION

) as PositionState;

const area = entity.components.find(

(c) => c.name === CompName.AREA

) as AreaState;

const mass = entity.components.find(

(c) => c.name === CompName.MASS

) as MassState;

const rect = entity.components.find(

(c) => c.name === CompName.RECT

) as RectState;

const Spritey = entity.components.find(

(c) => c.name === CompName.SPRITE

) as SpriteState;

const Color = entity.components.find(

(c) => c.name === CompName.COLOR

) as ColorState;

if (pos) {

//console.log("pos", pos.state.x.value, pos.state.y.value);

const spriteHeight = 50;

const spriteWidth = 50;

let areaOutlineRender = null;

let rectRender = null;

let entityRender = null;

if (Spritey) {

// console.log("Spritey", Spritey.state.sprite);

const sprite = ecs.getState().getSprite(Spritey.state.sprite)

//console.log("sprite", sprite);

entityRender = (

<Image

key={`sprite-${index}`}

x={pos.state.x}

y={pos.state.y}

width={spriteWidth}

height={spriteHeight}

image={sprite}

/>

);

}

if (rect) {

console.log("rect", rect.state.width, rect.state.height);

rectRender = (

<Rect

key={`rect-${index}`}

x={pos.state.x}

y={pos.state.y}

width={rect.state.width}

height={rect.state.height}

color={Color?.state.color}

/>

);

}

if (area) {

areaOutlineRender = (

<Rect

key={`stroke-${index}`}

x={pos.state.x}

y={pos.state.y}

width={area.state.width}

height={area.state.height}

style="stroke"

color="green"

strokeWidth={6}

/>

);

}

return [entityRender, areaOutlineRender, rectRender];

}

return []; // Return an empty array instead of void

})

}

</Canvas>

);

};

export default Render ;

const styles = StyleSheet.create({

container: {

flex: 1,

backgroundColor: "#fff",

alignItems: "center",

justifyContent: "center",

},

});

Expand full comment

I watch most of your video...and try to create them in react native...but it can be difficult when your using kaboom...and canvas...so it lead me to create a ECS ,where I create most of the components kaboom uses ...I just finish adding the scene to operate just like kaboom...and yes that's my plan,to open source it,so others can contribute to it.

Expand full comment

In my clone of kaboom liabray for react native i have to use custom events a lot, its very neat.

Expand full comment
author

How did you make a clone of Kaboom in React Native? I'm curious. Looks very interesting. Any plans to open source the library?

Expand full comment

'worklet'

// let y: number = 0;

// let x: number = 0;

const numOfRows: number = 211

const numOfCols: number = 15

let y: number = 0;

let x: number = 0;

const newArray: number[] = []

// addEntityToSystem(

// createEntity("ground", [

// Pos(0, 0),

// Rect(SCREEN_WIDTH, SCREEN_HEIGHT),

// Color("#f7d7db"),

// ])

// );

for (const layer of map.layers) {

if (layer.name === "collision") {

for (const object of layer.objects) {

if (object.name === "floor") {

const { x, y, width, height } = object;

addEntityToSystem(

createEntity("ground", [

//apply offset

Pos(x, MAP_OFFSET+y),

// Sprite("ground"),

// Rect(width, height),

Area(x, y ,

width,height),

Mass({ isStatic: true }),

])

);

continue;

}

}

}

if (layer.name === "spawnpoints") {

for (const object of layer.objects) {

if (object.name === "goomba") {

const { x, y, width, height } = object;

const goomba=

createEntity("goomb", [

Pos(x, y + MAP_OFFSET),

Sprite("bean"),

// Rect(width, height),

Area(x, y, 50, 50),

Mass(),

])

addEntityToSystem(goomba);

//setTree(goomba);

continue;

}

if (object.name === "player") {

const { x, y, width, height } = object;

enemy.current = createEntity("player", [

Pos(x, y + MAP_OFFSET),

Area(0, 0, 50, 50),

Mass(),

Rect(50, 50),

]);

addEntityToSystem(enemy.current);

continue;

}

}

}

}

}

export default world_1;

Expand full comment

import {

ColideEvents,

CompName,

Entity,

EntityComponentType,

MassState,

PositionState,

SystemT,

SystemType,

Vector2,

addEntityToSystem,

addSystem,

} from "../../types";

export interface RegistryI {

systems: { [key: string]: SystemT };

addEntityToSystem(entity: Entity): void;

//removeEntityFromSystem(entity: Entity): void;

addSystem(systemType: SystemType): void;

createEntity: (name: string, components: EntityComponentType[]) => Entity;

removeAllEntities: () => void;

colideEvents: ColideEvents;

emit:(event:string,data?:any) => void;

}

const useRegistry = create<RegistryI>((set, get) => ({

systems: {},

colideEvents:{},

addSystem: (systemType: SystemType) => {

"worklet";

const systemToAdd = Systems(systemType);

if (systemToAdd) {

set((state) => ({

systems: { ...state.systems, [systemType]: systemToAdd },

}));

}

},

addEntityToSystem: (entity: Entity) => {

"worklet";

const systems = get().systems;

Object.values(systems).forEach((system) => {

const meetsRequirements = system.requiredComponents.every((rc) =>

entity.components.some((c) => c.name === rc)

);

if (meetsRequirements) {

try {

set((state) => ({

systems: {

...state.systems,

[system.type]: {

...system,

entities: [...system.entities, entity],

},

},

}));

} catch (error) {

console.error(

"Error adding entity to system",

system.type,

entity,

error

);

}

}

});

},

createEntity: (name: string, components: EntityComponentType[]) => {

const newEntity:Entity = {

id: Math.random().toString(36).substring(7),

name,

components,

move: (direction: Vector2) => {

"worklet";

const position = components.find(

(c) => c.name === CompName.POSITION

) as PositionState;

const mass = components.find(

(c) => c.name === CompName.MASS

) as MassState;

if (position && mass) {

// console.log("Moving entity", name, "by", direction);

mass.state.velocity!.x.value = direction.x;

mass.state.velocity!.y.value += direction.y;

}

},

onCollide: (tag: string, callback: (data?:any) => void) => {

'worklet'

if (tag)

return;

if (!get().colideEvents[tag]) {

set((state) => ({

...state, colideEvents: {

...state.colideEvents[tag] = callback

}

}

))

}

}

};

return newEntity;

},

removeAllEntities: () => {

"worklet";

const systems = get().systems;

Object.values(systems).forEach((system) => {

set((state) => ({

systems: {

...state.systems,

[system.type]: {

...system,

entities: [],

},

},

}));

});

},

emit: (event: string, data?: any) => {

const callback = get().colideEvents[event];

if (callback) {

runOnJS(callback)(data);

}

}

}));

export default useRegistry;

Expand full comment

interface ScenceI {

[key: string]: () => void;

}

const App = () => {

const sencepool = useRef<ScenceI>({});

const Scence = useCallback((name: string, callback: () => void) => {

sencepool.current[name] = callback;

}, []);

const go = useCallback((name: string) => {

'worklet'

const callback = sencepool.current[name];

if (callback) {

callback();

}

}, []);

const direction = {

left: useSharedValue(false),

right: useSharedValue(false),

up: useSharedValue(false),

};

///mimic unity GetAxis where you cann call const move= GetAxis("Horizontal") or GetAxis("Vertical

const GetAxis = useCallback(

(axis: string): number => {

"worklet";

if (axis === "Horizontal") {

if (direction.left.value) {

return -1;

} else if (direction.right.value) {

return 1;

}

return 0;

}

if (axis === "Vertical") {

if (direction.up.value) {

return 1;

} else {

return 0;

}

}

return 0;

},

[direction]

);

const [player, setPlayer] = useState<Entity | undefined>(undefined);

const [tree, setTree] = useState<Entity | undefined>(undefined);

const enemy=useRef<Entity | undefined>(undefined);

const { SCREEN_HEIGHT, SCREEN_WIDTH } = useConstants();

var SPEED = 300; // Units per second

const JUMP_FORCE = 30; // Initial jump speed in units per second

let move: number = 0;

const {

addSystem,

systems,

addEntityToSystem,

createEntity,

removeAllEntities,

} = useRegistry();

const wait = (ms: number, func: () => void) => {

setTimeout(func, ms);

};

const scences: ScenceI = {

world_1: () =>

world_1(addEntityToSystem, createEntity, SCREEN_WIDTH, setPlayer,enemy),

world_2: () =>

world_2(removeAllEntities,addEntityToSystem,createEntity,SCREEN_WIDTH,setPlayer

),

};

useLoadSprite("bean", require("./assets/sprites/magic_mushroom.png"));

useLoadSprite("bird", require("./assets/sprites/bluebird-midflap.png"));

useLoadSprite("ground", require("./assets/sprites/tiles/bg.png"));

useEffect(() => {

addSystem(SystemType.RENDER);

addSystem(SystemType.MOVEMENT);

addSystem(SystemType.COLLISION_H);

addSystem(SystemType.GRAVITY);

addSystem(SystemType.COLLISION_V);

for (const key in scences) {

Scence(key, scences[key]);

}

go("world_1");

// enemy.current.move!(vector2(-1,0))

// enemy.current.onCollide!("player",()=>{

// console.log("collided with player")

// enemy.current?.move!(vector2(1,0))

// }

// )

}, [go, sencepool.current]);

let count = 0;

function SpawnTree() {

console.log("spawning tree");

const tree = createEntity("tree", [

Pos(SCREEN_WIDTH - 100, 250),

Area(100, 0, 50, 50),

Mass(),

Rect(50, 50),

]);

setTree(tree);

addEntityToSystem(tree);

console.log("spawning tree");

wait(1000 * 15, SpawnTree);

}

const update = (delta: number | null) => {

"worklet";

if (!delta) return;

if (systems[SystemType.MOVEMENT]) {

systems[SystemType.MOVEMENT].update(

delta,

systems[SystemType.MOVEMENT].entities

);

}

if (systems[SystemType.COLLISION_H]) {

systems[SystemType.COLLISION_H].update(

delta,

systems[SystemType.COLLISION_H].entities

);

}

if (systems[SystemType.GRAVITY]) {

systems[SystemType.GRAVITY].update(

delta,

systems[SystemType.GRAVITY].entities

);

}

if (systems[SystemType.COLLISION_V]) {

systems[SystemType.COLLISION_V].update(

delta,

systems[SystemType.COLLISION_V].entities

);

}

{

move = GetAxis("Horizontal");

// enemy.current?.move!({ x: move * 5, y: 0 });

enemy.current?.move!({ x: move * 5, y: 0 });

//tree.move!(vector2((-SPEED-- * delta) / 1000 / 3, 0));

if (direction.up.value) {

console.log("jumping");

//body.state.velocity!.y.value +=-30

// tree?.move!(vector2(0, -15));

enemy.current?.move!(vector2(0, -15));

const pos = enemy.current?.components.find(component => component.name === CompName.POSITION) as PositionState;

pos.state.isGrounded!.value = false;

direction.up.value = false;

}

}

};

useFrameCallback(({ timeSincePreviousFrame: delta }) => {

update(delta);

});

return (

<View style={styles.container}>

<Render />

<MovePad direction={direction} />

</View>

);

};

export default App;

Expand full comment

That's just some codes...but am able to create entity...add them to the scene...they then get drawn with react native skia canvas...so am getting 60fps... I got a render system that only render render able entity...I connect tile.io to it so I can design my tiles from there.... got all those inspiration from you and cris course...but I didn't want to leave react native ,so it cause me to build my own liabray lol

Expand full comment
author

That's nice. I was planning on starting to make games with React Native and was looking at React Native Skia.

Expand full comment