Refactored redux code and snake game

This commit is contained in:
2019-08-26 18:46:19 +02:00
parent fe7fae2c19
commit 918e5fee45
14 changed files with 1251 additions and 196 deletions

152
src/redux/game.js Normal file
View File

@@ -0,0 +1,152 @@
import Module from "./module.js";
export const MODULE_NAME = "GAME";
export const module = new Module(MODULE_NAME, {
loopId: null,
callers: [],
keyPressSubscribers: [],
fps: 5
});
export const keys = {
UP: "ArrowUp",
RIGHT: "ArrowRight",
DOWN: "ArrowDown",
LEFT: "ArrowLeft",
PLUS: "+",
MINUS: "-"
};
export const [START_LOOP, startLoop] = module.action("START_LOOP");
export const [STOP_LOOP, stopLoop] = module.action("STOP_LOOP");
export const [LOOP, loop] = module.action("LOOP", ({ recur, skip } = {}) => ({ recur, skip }));
export const [UPDATE_LOOP_ID, updateLoopId] = module.action("UPDATE_LOOP_ID", loopId => ({ loopId }));
export const [SET_FPS, setFps] = module.action("SET_FPS", fps => ({ fps }));
export const [INVOKE_CALLERS, invokeCallers] = module.action("INVOKE_CALLERS");
export const [REGISTER_CALLER, registerCaller] = module.action("REGISTER_CALLER", name => ({ name }));
export const [UNREGISTER_CALLER, unregisterCaller] = module.action("UNREGISTER_CALLER", name => ({ name }));
export const [KEY_PRESS, keyPress] = module.action("KEY_PRESS", key => ({ key }));
export const [SUBSCRIBE_KEY_PRESSED, subscribeKeyPressed] = module.action("SUBSCRIBE_KEY_PRESSED", name => ({ name }));
export const [UNSUBSCRIBE_KEY_PRESSED, unsubscribeKeyPressed] = module.action("UNSUBSCRIBE_KEY_PRESSED", name => ({
name
}));
module.reducer(UPDATE_LOOP_ID, (state, { loopId }) => {
return { ...state, loopId };
});
module.reducer(SET_FPS, (state, { fps }) => {
return { ...state, fps };
});
module.reducer(REGISTER_CALLER, (state, { name }) => {
if (!name) {
throw new Error("Argument 'name' is required.");
}
if (typeof name !== "string") {
throw new Error("Argument 'name' is must be a function.");
}
if (state.callers.includes(name)) {
return state;
}
return { ...state, callers: [...state.callers, name] };
});
module.reducer(UNREGISTER_CALLER, (state, { name }) => {
if (!name) {
throw new Error("Argument 'name' is required.");
}
if (typeof name !== "string") {
throw new Error("Argument 'name' is must be a function.");
}
if (!state.callers.includes(name)) {
return state;
}
return { ...state, callers: state.callers.filter(caller => caller !== name) };
});
module.reducer(SUBSCRIBE_KEY_PRESSED, (state, { name }) => {
if (!name) {
throw new Error("Argument 'name' is required.");
}
if (typeof name !== "string") {
throw new Error("Argument 'name' is must be a function.");
}
if (state.keyPressSubscribers.includes(name)) {
return state;
}
return { ...state, keyPressSubscribers: [...state.keyPressSubscribers, name] };
});
module.reducer(UNSUBSCRIBE_KEY_PRESSED, (state, { name }) => {
if (!name) {
throw new Error("Argument 'name' is required.");
}
if (typeof name !== "string") {
throw new Error("Argument 'name' is must be a function.");
}
if (!state.keyPressSubscribers.includes(name)) {
return state;
}
return { ...state, keyPressSubscribers: state.keyPressSubscribers.filter(subscriber => subscriber !== name) };
});
module.middleware(KEY_PRESS, (dispatch, { keyPressSubscribers = [], fps }, { key }) => {
keyPressSubscribers.forEach(subscriber => dispatch({ type: subscriber, key }));
if (key === keys.PLUS) {
dispatch(setFps(fps + 1));
}
if (key === keys.MINUS) {
dispatch(setFps(fps - 1));
}
});
module.middleware(INVOKE_CALLERS, (dispatch, { callers = [] }) => {
callers.forEach(caller => dispatch({ type: caller }));
});
module.middleware(START_LOOP, (dispatch, { loopId, callers, fps }) => {
if (loopId) {
return;
}
dispatch(loop());
});
module.middleware(LOOP, (dispatch, { loopId, fps }, { skip = 0, recur = false }, getState) => {
if (loopId) {
return;
}
let start = null;
const loop = ({ recur = false, skip = 0 } = {}) => {
const { loopId, fps } = getState()[MODULE_NAME];
if (recur && !loopId) {
return null;
}
return requestAnimationFrame(timestamp => {
const minDuration = 1000 / fps;
if (!start) {
start = timestamp;
}
const progress = timestamp - start;
if (progress === 0 || progress > minDuration) {
start = timestamp;
dispatch(invokeCallers());
}
return loop({ recur: true, skip: skip > 10 ? 0 : skip + 1 });
});
};
dispatch(updateLoopId(loop()));
});
module.middleware(STOP_LOOP, (dispatch, { loopId }) => {
if (!loopId) {
return;
}
dispatch(updateLoopId(null));
});
export default module;