Commit b42d184f authored by wangzhujun's avatar wangzhujun
parents 90e36ef6 904e0c9b
......@@ -25,7 +25,7 @@ export abstract class Element extends Container {
this.sp = this.addChild(new Sprite(Assets.get(texture)));
this.sp.anchor.set(0.5);
this.angle = Math.random() * 360;
this.sp.angle = Math.random() * 360;
this.initUI();
this.initPhy();
......
......@@ -28,7 +28,7 @@ export class Filiform extends Element {
}
);
this.body.setOffset(new SATVector(-width / 2, -height / 2));
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
......
......@@ -28,7 +28,7 @@ export class Molecule extends Element {
}
);
this.body.setOffset(new SATVector(-width / 2, -height / 2));
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
const { width, height } = this.sp;
......
......@@ -2,7 +2,8 @@ import { Element } from "@/pages/GamePage/Components/Element/Element.ts";
import { Ele } from "@/pages/GamePage/config/Config.ts";
import { collisionSys } from "@/pages/GamePage/GamePage.tsx";
import { SATVector } from "detect-collisions";
import { DEG_TO_RAD } from "pixi.js";
import { Assets, DEG_TO_RAD, PointData, Sprite } from "pixi.js";
import { Tween } from "@/pages/GamePage/tween";
/**
* 艾草
......@@ -10,8 +11,20 @@ import { DEG_TO_RAD } from "pixi.js";
export class Mugwort extends Element {
id: Ele = Ele.Mugwort;
dgTip: Sprite;
initUI(): void {
this.dgTip = this.addChild(new Sprite(Assets.get('元素/危险.png')));
this.dgTip.angle = -this.angle;
this.dgTip.anchor.set(0.5, 0.5);
this.dgTip.x = 28;
Tween.get(this.dgTip, {
loop: true,
}).to({ alpha: 0.2 }, 444)
.to({ alpha: 1 }, 444);
}
initPhy(): void {
......@@ -27,10 +40,20 @@ export class Mugwort extends Element {
}
);
this.body.setOffset(new SATVector(-width / 2.1 / 2, -height / 2.1 / 2));
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
const { width, height } = this.sp;
this.body.setOffset(new SATVector(-width / 2.1 / 2, -height / 2.1 / 2));
}
onDestroy() {
Tween.removeTweens(this.dgTip);
}
beEaten(pos: PointData){
this.dgTip.visible = false;
super.beEaten(pos);
}
}
......@@ -2,7 +2,8 @@ import { Element } from "@/pages/GamePage/Components/Element/Element.ts";
import { Ele } from "@/pages/GamePage/config/Config.ts";
import { collisionSys } from "@/pages/GamePage/GamePage.tsx";
import { SATVector } from "detect-collisions";
import { DEG_TO_RAD } from "pixi.js";
import { Assets, DEG_TO_RAD, PointData, Sprite } from "pixi.js";
import { Tween } from "@/pages/GamePage/tween";
/**
* 雄黄
......@@ -10,8 +11,20 @@ import { DEG_TO_RAD } from "pixi.js";
export class Realgar extends Element {
id: Ele = Ele.Realgar;
dgTip: Sprite;
initUI(): void {
this.dgTip = this.addChild(new Sprite(Assets.get('元素/危险.png')));
this.dgTip.angle = -this.angle;
this.dgTip.anchor.set(0.5, 0.5);
this.dgTip.x = 28;
Tween.get(this.dgTip, {
loop: true,
}).to({ alpha: 0.2 }, 444)
.to({ alpha: 1 }, 444);
}
initPhy(): void {
......@@ -27,10 +40,20 @@ export class Realgar extends Element {
}
);
this.body.setOffset(new SATVector(-width / 2.1 / 2, -height / 2.6 / 2));
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
const { width, height } = this.sp;
this.body.setOffset(new SATVector(-width / 2.1 / 2, -height / 2.6 / 2))
}
onDestroy() {
Tween.removeTweens(this.dgTip);
}
beEaten(pos: PointData){
this.dgTip.visible = false;
super.beEaten(pos);
}
}
......@@ -23,7 +23,7 @@ export class Yeast extends Element {
}
}
);
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
......
......@@ -16,14 +16,14 @@ export class ZongZi extends Element {
initPhy(): void {
this.body = collisionSys.createCircle(
this.getGlobalPosition(),
this.sp.height / 2 / 2.2,
this.sp.height / 2 / 2.3,
{
userData: {
ele: this
}
}
);
this.body.setAngle(this.angle * DEG_TO_RAD);
this.body.setAngle(this.sp.angle * DEG_TO_RAD);
}
updatePhy(): void {
......
......@@ -5,12 +5,15 @@ import { EleConfig, EleData, mapSize } from "@/pages/GamePage/config/Config.ts";
const foodWeight = new Weight<EleData>();
const negWeight = new Weight<EleData>();
for (let eleConfigKey in EleConfig) {
const { neg, weight } = EleConfig[eleConfigKey];
if (neg) {
negWeight.add(EleConfig[eleConfigKey], weight);
} else {
foodWeight.add(EleConfig[eleConfigKey], weight);
function initWeight() {
for (let eleConfigKey in EleConfig) {
const { neg, weight } = EleConfig[eleConfigKey];
if (neg) {
negWeight.add(EleConfig[eleConfigKey], weight);
} else {
foodWeight.add(EleConfig[eleConfigKey], weight);
}
}
}
......@@ -25,17 +28,19 @@ export class ElementMgr {
this.footCtn = this.root.addChild(new Container());
this.negCtn = this.root.addChild(new Container());
initWeight();
}
start() {
Tween.get(this, { loop: true })
.wait(1000)
.wait(1500)
.call(() => {
this.flushFood();
});
Tween.get(this, { loop: true })
.wait(1500)
.wait(2000)
.call(() => {
this.flushNeg();
})
......
import { Assets, Container, Sprite, Text } from "pixi.js";
import { Ease, Tween } from "@/pages/GamePage/tween";
export class ScoreBubble extends Container {
constructor(score: number) {
super();
this.initUI(score);
}
initUI(score: number) {
const sp = this.addChild(new Sprite(Assets.get("蛇/初级/圈.png")));
sp.anchor.set(0.5, 0.5);
const text = this.addChild(new Text({
text: `${score > 0 ? "+" : ""}${score}`,
style: {
fontSize: 22,
fill: 0xebe7cc,
align: "center",
}
}));
text.anchor.set(0.5, 0.5);
this.scale.set(0, 0);
Tween.get(this.scale)
.to({ x: 1, y: 1 }, 222, Ease.backOut)
.wait(500)
.call(() => {
this.removeFromParent();
});
}
}
import { Assets, Container, Point, PointData, Sprite } from "pixi.js";
import { mapSize } from "@/pages/GamePage/config/Config.ts";
import { GameConfig, mapSize } from "@/pages/GamePage/config/Config.ts";
import { Circle } from "detect-collisions";
import { collisionSys } from "@/pages/GamePage/GamePage.tsx";
import { Tween } from "@/pages/GamePage/tween";
import { IReactionDisposer, reaction } from "mobx";
import gameStore from "@/store/gameStore.ts";
export class Snake extends Container {
head: Sprite = null;
......@@ -11,20 +14,6 @@ export class Snake extends Container {
bodyScale: number = 1;
energy: number = 0;
private _length: number = 0;
get length() {
return this._length;
}
set length(value: number) {
this._length = value;
this.emit("updateLength", value);
}
get radius() {
return this.bodyScale * 46 / 2;
}
// 位置相关
private ready: boolean = false;
isLife: boolean = true;
......@@ -34,10 +23,35 @@ export class Snake extends Container {
dir: Point = new Point(1, 0);
collider: Circle = null;
negCollider: Circle = null;
levelDisposer: IReactionDisposer = null;
init(){
init() {
this.initUI();
this.initPhy();
this.levelDisposer = reaction(
() => gameStore.gameInfo.level,
(level) => {
const { levelCfg } = GameConfig;
const { skin } = levelCfg[level];
this.setSkin(skin);
},
{ fireImmediately: true }
);
}
destroy() {
this.levelDisposer();
super.destroy();
}
setSkin(skin: string) {
this.bodyScale = 1;
this.head.texture = Assets.get(`蛇/${skin}/head.png`);
this.bodyArr.forEach((body, i) => {
body.texture = Assets.get(`蛇/${skin}/body${i % 2}.png`);
});
}
initUI() {
......@@ -57,10 +71,8 @@ export class Snake extends Container {
this.head.position.set(375, 375);
this.head.anchor.set(0.5, 0.5);
this.length = 1;
// 创建身体节点
this.addEnergy(50);
this.addEnergy(GameConfig.initEnergy);
this.isLife = true;
this.ready = true;
......@@ -68,55 +80,59 @@ export class Snake extends Container {
initPhy() {
const p = this.head.getGlobalPosition();
this.collider = collisionSys.createCircle(p, this.radius);
this.collider = collisionSys.createCircle(p, 46 / 2, {
userData: {
snake: true,
isNeg: false,
}
});
this.collider.setScale(this.bodyScale, this.bodyScale);
this.negCollider = collisionSys.createCircle(p, 46 / 2, {
userData: {
snake: true,
isNeg: true,
}
});
this.negCollider.setScale(this.bodyScale, this.bodyScale);
}
updatePhy() {
const p = this.head.getGlobalPosition();
this.collider.setPosition(p.x, p.y);
this.collider.setScale(this.bodyScale, this.bodyScale);
}
onEnable() {
}
let scale = this.bodyScale;
if (this.magnetInfo.time) {
scale *= GameConfig.magnetScale;
}
this.collider.setScale(scale, scale);
onDisable() {
this.negCollider.setPosition(p.x, p.y);
this.negCollider.setScale(this.bodyScale, this.bodyScale);
}
// 上次生长富余的能量
private lastRemaining = 0;
/**
* 加能量
* 暂定加长/双倍道具卡效果不叠加
*/
addEnergy(value: number) {
this.energy += value
const growthThreshold = Math.floor(12 * this.bodyScale);
const { growthThreshold } = GameConfig;
this.energy = Math.max(this.energy + value, 0);
value += this.lastRemaining;
const originLen = this.bodyArr.length;
const len = this.energy / growthThreshold >> 0;
while (value >= growthThreshold) {
value -= growthThreshold;
if (this.bodyScale < 3) {
this.bodyScale += 0.01;
if (originLen < len) {
for (let i = originLen; i < len; i++) {
this.grow();
}
this.grow();
} else if (originLen > len) {
this.bodyArr
.splice(len, originLen - len)
.forEach((body) => {
body.removeFromParent();
});
}
this.lastRemaining = value;
// this.speed = 600 * this.scale;
}
/**
* 快速生长
*/
fastGrow(energy: number) {
this.addEnergy(energy)
// for (let i = 0; i < time; i++) {
// this.grow();
// }
}
/**
......@@ -124,39 +140,28 @@ export class Snake extends Container {
*/
private grow() {
if (this.length > 180) {
return;
}
if (!this.isLife) return;
this.length += 1;
let len = this.bodyArr.length;
const newBody = new Sprite(Assets.get("蛇/初级/body0.png"));
const { levelCfg } = GameConfig;
const { skin } = levelCfg[gameStore.gameInfo.level];
const newBody = new Sprite(Assets.get(`蛇/${skin}/body${len % 2}.png`));
const pre = this.bodyArr[len - 1] || this.head;
newBody.angle = pre.angle;
newBody.position.set(999999, 999999);
newBody.position.set(pre.x, pre.y);
newBody.scale.set(this.bodyScale, this.bodyScale);
this.addChildAt(newBody, 0);
newBody.anchor.set(0.5, 0.5);
// newBody.active = isIntersect(
// newBody.getPosition(),
// this.head.getPosition(),
// this.vw,
// this.vh
// );
this.bodyArr.splice(len, 0, newBody);
}
positions: PointData[] = []; // 存储历史位置点
private readonly HISTORY_LENGTH = 10; // 增加历史点数量
private readonly SEGMENT_SPACING = 6.3; // 增加节点间距
private readonly SEGMENT_SPACING = 5; // 增加节点间距
moveTime = 1 / 60;
totalTime = 0;
......@@ -229,36 +234,27 @@ export class Snake extends Container {
body.scale.set(this.bodyScale, this.bodyScale);
}
}
}
// // 边界检查
// const mapHalfWidth = Global.HALF_MAP_WIDTH;
// const mapHalfHeight = Global.HALF_MAP_HEIGHT;
// if (
// newHeadPos.x < -mapHalfWidth
// || newHeadPos.x > mapHalfWidth
// || newHeadPos.y < -mapHalfHeight
// || newHeadPos.y > mapHalfHeight
// ) {
// this.death();
// }
/****************************** 磁铁 ******************************/
magnetInfo = {
time: 0,
}
/****************************** 磁铁 ******************************/
useMagnet() {
this.clearMagnet();
// this.magnetEffectNode.active = true;
// this.magnetEffectNode.getComponent(Animation).play();
// this.eatCollider.radius = Global.PROP_MAGNET_RADIUS;
// this.scheduleOnce(this.clearMagnet, Global.PROP_MAGNET_DUR_TIME)
this.magnetInfo.time = GameConfig.magnetTime;
Tween.get(this.magnetInfo)
.to({ time: 0 }, this.magnetInfo.time)
.call(() => {
this.clearMagnet();
});
}
clearMagnet() {
// this.unschedule(this.clearMagnet)
// this.magnetEffectNode.active = false;
// this.magnetEffectNode.getComponent(Animation).stop();
// this.eatCollider.radius = 65;
Tween.removeTweens(this.magnetInfo);
this.magnetInfo.time = 0;
}
}
import { Assets, Container, Sprite, Ticker } from "pixi.js";
import { Assets, Container, PointData, Sprite, Ticker } from "pixi.js";
import bgImg from "@/assets/GamePage/bg.jpg";
import { Joystick } from "@/pages/GamePage/Components/Joystick.ts";
import 'pixi.js/math-extras';
import { Snake } from "@/pages/GamePage/Components/Snake.ts";
import { ElementMgr } from "@/pages/GamePage/Components/ElementMgr.ts";
import { mapTop } from "@/pages/GamePage/config/Config.ts";
import { Ele, EleConfig, mapTop } from "@/pages/GamePage/config/Config.ts";
import { collisionSys } from "@/pages/GamePage/GamePage.tsx";
import {Element} from "./Components/Element/Element.ts";
import { Response, Body } from "detect-collisions";
import gameStore from "@/store/gameStore.ts";
import { ScoreBubble } from "@/pages/GamePage/Components/ScoreBubble.ts";
export class Game extends Container {
......@@ -64,15 +66,44 @@ export class Game extends Container {
overlap, overlapV, overlapN
} = result;
const { userData: { ele } } = b as Body<{ele: Element}>;
const { userData: aData } = a as Body<{ ele: Element, snake: boolean, isNeg: boolean }>;
const { userData: bData } = b as Body<{ ele: Element, snake: boolean, isNeg: boolean }>;
if (aData?.snake == bData?.snake) return;
const ele = aData?.ele || bData?.ele;
const { id } = ele;
const { score, neg } = EleConfig[id];
const isNeg = !!(aData?.isNeg || bData?.isNeg);
if (isNeg != (!!neg)) return;
ele.beEaten(this.snake.head);
if (id === Ele.ZongZi) {
this.snake.useMagnet();
} else {
this.snake.addEnergy(score);
this.addScore(score, ele.position);
}
}
addScore(score: number, pos: PointData) {
gameStore.addScore(score);
const bubble = this.mapCtn.addChild(new ScoreBubble(score));
bubble.position.set(pos.x + 20, pos.y - 20);
}
onUpdate(time: Ticker) {
const dt = time.deltaMS / 1000;
this.snake.dir.copyFrom(this.joystick.dir);
this.snake.onUpdate(dt);
collisionSys.checkOne(this.snake.collider, this.colliderCallback);
collisionSys.checkAll(this.colliderCallback);
}
destroy() {
......
......@@ -16,9 +16,10 @@ import { Stats } from 'pixi-stats';
import { System } from 'detect-collisions';
import gameStore from "@/store/gameStore.ts";
import { prefixInteger, second2Date } from "@/utils/utils.ts";
export const collisionSys: System = new System();
const DEBUG = true;
const DEBUG = true ;
export function getApp(): Application {
return window["__app"];
......@@ -41,16 +42,34 @@ class GamePage extends React.Component {
app: Application = null;
game: Game = null;
interval: any = 0;
async componentDidMount() {
gameStore.reset();
await initBundle();
this.initEvent();
this.debugCtx = this.debugCanvas.getContext("2d");
this.debugCanvas.width = winSize.width;
this.debugCanvas.height = winSize.height;
if (DEBUG) {
this.debugCtx = this.debugCanvas.getContext("2d");
this.debugCanvas.width = winSize.width;
this.debugCanvas.height = winSize.height;
}
await this.initStage();
this.startCd();
}
startCd = () => {
this.interval = setInterval(() => {
gameStore.gameInfo.cd -= 1;
if (gameStore.gameInfo.cd <= 0) {
clearInterval(this.interval);
gameStore.gameInfo.cd = 0;
}
}, 1000);
}
initEvent() {
......@@ -60,6 +79,7 @@ class GamePage extends React.Component {
this.app?.destroy();
window["__app"] = null;
collisionSys.clear();
Tween.removeAllTweens();
}
async initStage() {
......@@ -123,7 +143,10 @@ class GamePage extends React.Component {
render() {
const { score } = gameStore.gameInfo;
const { score, cd } = gameStore.gameInfo;
const min = prefixInteger(Math.floor(cd / 60), 2);
const sec = prefixInteger(cd % 60, 2);
return <div className="GamePage">
<div className="gameBg"/>
......@@ -141,7 +164,7 @@ class GamePage extends React.Component {
<div className="scoreNum">{score}</div>
</div>
<div className="cd">
<div className="cdNum">1:20</div>
<div className="cdNum">{min}:{sec}</div>
</div>
<MusicBtn className="musicBtn"/>
<Button className="backBtn" onClick={this.clickBack}/>
......
......@@ -19,41 +19,77 @@ export interface EleData {
cls: new () => Element,
texture: string,
weight: number,
score?: number,
neg?: boolean,
}
export const GameConfig = {
gameCd: 120,
magnetTime: 10000,
magnetScale: 4,
initEnergy: 10,
growthThreshold: 2,
levelCfg: [
{
score: 0,
skin: "初级",
level: 0,
},
{
score: 30,
skin: "红色",
level: 1,
},
{
score: 60,
skin: "黄色",
level: 2,
},
{
score: 90,
skin: "蓝色",
level: 3,
},
],
}
export const EleConfig: { [key in Ele]: EleData } = {
[Ele.Yeast]: {
cls: Yeast,
texture: "元素/酵母菌.png",
weight: 2,
weight: 4,
score: 1,
},
[Ele.Filiform]: {
cls: Filiform,
texture: "元素/丝状菌.png",
weight: 2,
weight: 3,
score: 20,
},
[Ele.Molecule]: {
cls: Molecule,
texture: "元素/黄色分子.png",
weight: 2,
score: 3,
},
[Ele.Mugwort]: {
cls: Mugwort,
texture: "元素/艾草.png",
weight: 1,
score: -2,
neg: true,
},
[Ele.Realgar]: {
cls: Realgar,
texture: "元素/雄黄.png",
weight: 1,
score: -2,
neg: true,
},
[Ele.ZongZi]: {
cls: ZongZi,
texture: "元素/粽子.png",
weight: 100,
weight: 1,
}
}
......
......@@ -5,6 +5,7 @@ import {Toast} from "@grace/ui";
import {AESEncrypt} from "@/utils/Crypto.ts";
import { PageCtrl } from "@/core/ctrls/PageCtrl.tsx";
import HomePage from "@/pages/HomePage/HomePage.tsx";
import { GameConfig } from "@/pages/GamePage/config/Config.ts";
class GameStore {
......@@ -39,15 +40,45 @@ class GameStore {
gameInfo: {
score: number,
remainTimes: number,
level: number,
maxScore: number,
cd: number,
} = {
score: 0,
remainTimes: 0,
level: 0,
maxScore: 0,
cd: GameConfig.gameCd,
}
async submit(win: boolean, soc: number) {
reset() {
this.gameInfo = {
score: 0,
remainTimes: 0,
level: 0,
maxScore: 0,
cd: GameConfig.gameCd,
}
}
addScore(s: number) {
const score = this.gameInfo.score + s;
this.gameInfo.score = score;
this.gameInfo.maxScore = Math.max(score, this.gameInfo.maxScore);
const { levelCfg } = GameConfig;
for (let i = levelCfg.length - 1; i >= 0; i--) {
if (score >= levelCfg[i].score) {
this.gameInfo.level = i;
break;
}
}
}
async submit(score: number) {
const startId = this.startInfo.userRecordId;
const score = win ? soc : 0;
const params: {
startId: string | number,
......@@ -72,7 +103,7 @@ class GameStore {
store.indexData.remainTimes = data.remainGameTimes;
if (win && data) {
if (data) {
} else {
}
......
......@@ -413,3 +413,8 @@ export const getCustomShareId = () => {
console.log('自定义活动页id', shareId);
return shareId;
};
export function prefixInteger(num: number, length: number) {
return (Array(length).join('0') + num).slice(-length);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment