Make some schnecken
This commit is contained in:
parent
f205d23179
commit
0eed2881ad
BIN
src/assets/schnecke.png
Normal file
BIN
src/assets/schnecke.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
|
|
@ -164,13 +164,13 @@ export class Vec {
|
|||
distance(x: number, y: number): number;
|
||||
distance(x: number|Vec, y?: number): number {
|
||||
if (x instanceof Vec) {
|
||||
return this.sub(x).length();
|
||||
return Math.sqrt((this.x - x.x) * (this.x - x.x) + (this.y - x.y) * (this.y - x.y));
|
||||
}
|
||||
if (undefined === y) {
|
||||
throw new Error("y-coordinate undefined on vector operation");
|
||||
}
|
||||
|
||||
return this.sub(x, y).length();
|
||||
return Math.sqrt((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y));
|
||||
}
|
||||
|
||||
rotate(angle: number) {
|
||||
|
|
|
|||
1
src/env.d.ts
vendored
Normal file
1
src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module "*.png";
|
||||
|
|
@ -9,6 +9,7 @@ const gravity = 0.008;
|
|||
const numParticles = 200;
|
||||
const pressureForce = 8000;
|
||||
export const particleSize = 10;
|
||||
export const imageSize = 64;
|
||||
export const desiredDensity = 6;
|
||||
const densityRadius = 40;
|
||||
const mass = 1;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Canvas } from "@/canvas";
|
||||
import { GameObject } from "@/common/gameobject";
|
||||
import { Vec } from "@/common/vec";
|
||||
import { particleSize } from "@/game/fluids/fluids";
|
||||
import { imageSize, particleSize } from "@/game/fluids/fluids";
|
||||
import { Swarm } from "@/game/swarm/swarm";
|
||||
import { fillCircle, strokeCircle, strokeLine } from "@/graphics";
|
||||
|
||||
import { drawImageRotated, fillCircle, strokeCircle, strokeLine } from "@/graphics";
|
||||
import Schnecke from "@/assets/schnecke.png";
|
||||
|
||||
const schneckenImage = new Image();
|
||||
schneckenImage.src = Schnecke;
|
||||
export type NavigationType = 'stayInBounds' | 'doNotBumpIntoFlock' | 'keepFlockClose' | 'behaveLikeFlockDoes' | 'idling';
|
||||
export type NavigatingDesire = {
|
||||
pos: Vec;
|
||||
|
|
@ -117,14 +119,20 @@ export class Agent extends GameObject {
|
|||
|
||||
doNotBumpIntoFlock(nextPos: Vec): NavigatingDesire {
|
||||
const {agents} = this.swarm;
|
||||
const visibleAgentsNotMe = agents
|
||||
.filter(a => a.pos.distance(this.pos) < this.swarm.config.flockPerceptionDistance && a !== this);
|
||||
const visibleAgentsInBounds = visibleAgentsNotMe
|
||||
.filter(a => this.swarm.rect.contains(a.pos));
|
||||
const visibleAgentsInBounds = [];
|
||||
let closestAgent: Agent|null = null;
|
||||
for (const a of visibleAgentsInBounds) {
|
||||
if (!closestAgent || a.pos.distance(this.pos) < closestAgent.pos.distance(this.pos)) {
|
||||
let closestDist: number = Infinity;
|
||||
for (const a of agents) {
|
||||
const dist = a.pos.distance(this.pos);
|
||||
if (!this.swarm.rect.contains(a.pos)
|
||||
|| dist >= this.swarm.config.flockPerceptionDistance
|
||||
|| a === this) {
|
||||
continue;
|
||||
}
|
||||
visibleAgentsInBounds.push(a);
|
||||
if (!closestAgent || dist < closestDist) {
|
||||
closestAgent = a;
|
||||
closestDist = dist;
|
||||
}
|
||||
}
|
||||
if (!closestAgent) {
|
||||
|
|
@ -135,7 +143,7 @@ export class Agent extends GameObject {
|
|||
};
|
||||
}
|
||||
const awayFromClosestAgentDir = this.pos.sub(closestAgent.pos).normalize();
|
||||
const urgency = Math.max(0, (this.swarm.config.desiredDistanceToEveryone - closestAgent.pos.distance(this.pos)) / this.swarm.config.desiredDistanceToEveryone);
|
||||
const urgency = Math.max(0, (this.swarm.config.desiredDistanceToEveryone - closestDist) / this.swarm.config.desiredDistanceToEveryone);
|
||||
return {
|
||||
pos: this.pos.add(awayFromClosestAgentDir),
|
||||
urgency: urgency * urgency,
|
||||
|
|
@ -145,10 +153,9 @@ export class Agent extends GameObject {
|
|||
|
||||
keepFlockClose(nextPos: Vec): NavigatingDesire {
|
||||
const {agents} = this.swarm;
|
||||
const visibleAgents = agents
|
||||
const visibleAgentsInBounds = agents
|
||||
.filter(a => this.swarm.rect.contains(a.pos))
|
||||
.filter(a => a.pos.distance(this.pos) < this.swarm.config.flockPerceptionDistance);
|
||||
const visibleAgentsInBounds = visibleAgents
|
||||
.filter(a => this.swarm.rect.contains(a.pos));
|
||||
const center = visibleAgentsInBounds
|
||||
.reduce((sum, a) => sum.add(a.pos), new Vec)
|
||||
.scale(1 / visibleAgentsInBounds.length);
|
||||
|
|
@ -203,17 +210,27 @@ export class Agent extends GameObject {
|
|||
}
|
||||
|
||||
render({ctx, input}: Canvas) {
|
||||
if (input.mouseDown) {
|
||||
if (this === this.swarm.selectedAgent) {
|
||||
console.log(this.dir.clockwiseAngleBetween(1, 0));
|
||||
}
|
||||
if (this.swarm.config.renderStyle === 'image') {
|
||||
const satisfaction = Math.floor(this.satisfaction());
|
||||
const dissatisfaction = 255 - satisfaction;
|
||||
// draw color
|
||||
ctx.filter = `hue-rotate(${satisfaction}deg) saturate(1000%)`;
|
||||
|
||||
// set composite mode
|
||||
drawImageRotated(ctx, schneckenImage, -this.dir.clockwiseAngleBetween(1, 0), this.pos.x, this.pos.y, imageSize, imageSize);
|
||||
ctx.filter = `none`;
|
||||
} else if (input.mouseDown) {
|
||||
fillCircle(ctx, this.pos, particleSize, 'gray');
|
||||
} else {
|
||||
if (this.swarm.config.renderStyle === 'satisfaction') {
|
||||
const satisfaction = this.satisfaction();
|
||||
const dissatisfaction = 255 - satisfaction;
|
||||
fillCircle(ctx, this.pos, particleSize, `rgb(${dissatisfaction}, 0, ${satisfaction})`);
|
||||
} else {
|
||||
const color = navigationColorMap[this.lastNavigation.type];
|
||||
fillCircle(ctx, this.pos, particleSize, color);
|
||||
}
|
||||
} else if (this.swarm.config.renderStyle === 'satisfaction') {
|
||||
const satisfaction = this.satisfaction();
|
||||
const dissatisfaction = 255 - satisfaction;
|
||||
fillCircle(ctx, this.pos, particleSize, `rgb(${dissatisfaction}, 0, ${satisfaction})`);
|
||||
} else if (this.swarm.config.renderStyle === 'navigationMode') {
|
||||
const color = navigationColorMap[this.lastNavigation.type];
|
||||
fillCircle(ctx, this.pos, particleSize, color);
|
||||
}
|
||||
if (this.swarm.config.showDirections) {
|
||||
strokeLine(ctx, this.pos, this.pos.add(this.dir.scale(30)));
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Agent } from "@/game/swarm/agent";
|
|||
import { strokeCircle } from "@/graphics";
|
||||
import Button from "@/ui/button";
|
||||
|
||||
export type RenderStyle = 'satisfaction'|'navigationMode';
|
||||
export type RenderStyle = 'satisfaction'|'navigationMode'|'image';
|
||||
|
||||
export class Config {
|
||||
numAgents: number = 20;
|
||||
|
|
@ -15,7 +15,7 @@ export class Config {
|
|||
idlingUrgency: number = 0.001;
|
||||
idlingDistance: number = 20;
|
||||
desiredDistanceToEveryone: number = 50;
|
||||
renderStyle: RenderStyle = 'satisfaction';
|
||||
renderStyle: RenderStyle = 'image';
|
||||
showDirections: boolean = false;
|
||||
showFlockCenter: boolean = false;
|
||||
flockPerceptionDistance: number = 100;
|
||||
|
|
@ -64,15 +64,23 @@ export class Swarm extends GameObject {
|
|||
const buttons = [
|
||||
new Button("Reset", Rect.bySize(new Vec(10, 10), buttonSize), () => this.init(canvas)),
|
||||
new Button("Toggle Colors", Rect.bySize(new Vec(110, 10), buttonSize), () => {
|
||||
this.config.renderStyle = this.config.renderStyle === 'satisfaction' ? 'navigationMode' : 'satisfaction';
|
||||
const styles: RenderStyle[] = ['satisfaction', 'navigationMode', 'image'];
|
||||
this.config.renderStyle = styles[(styles.indexOf(this.config.renderStyle) + 1) % styles.length];
|
||||
}),
|
||||
new Button("Less Agents", Rect.bySize(new Vec(10, 60), buttonSize), () => {
|
||||
this.config.numAgents -= Math.max(1, this.config.numAgents / 10);
|
||||
this.init(canvas);
|
||||
const agentsToRemove = Math.max(1, this.config.numAgents / 10);
|
||||
this.agents.splice(-agentsToRemove, agentsToRemove).forEach(a => canvas.remove(a));
|
||||
this.config.numAgents -= agentsToRemove;
|
||||
}),
|
||||
new Button("More Agents", Rect.bySize(new Vec(110, 60), buttonSize), () => {
|
||||
this.config.numAgents += Math.max(1, this.config.numAgents / 10);
|
||||
this.init(canvas);
|
||||
const agentsToAdd = Math.max(1, this.config.numAgents / 10);
|
||||
for (let i = 0; i < agentsToAdd; i++) {
|
||||
const pos = Vec.random(this.rect);
|
||||
const agent = new Agent(pos, this);
|
||||
this.agents.push(agent);
|
||||
canvas.add(agent);
|
||||
}
|
||||
this.config.numAgents += agentsToAdd;
|
||||
}),
|
||||
new Button("toggle Directions", Rect.bySize(new Vec(10, 110), buttonSize), () => {
|
||||
this.config.showDirections = !this.config.showDirections;
|
||||
|
|
@ -101,6 +109,11 @@ export class Swarm extends GameObject {
|
|||
}
|
||||
|
||||
update(canvas: Canvas, delta: DOMHighResTimeStamp) {
|
||||
if (canvas.input.keypressed('Escape')) {
|
||||
canvas.camera.reset();
|
||||
canvas.camera.unlock();
|
||||
this.selectedAgent = null;
|
||||
}
|
||||
}
|
||||
|
||||
render(canvas: Canvas) {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ export function drawImageRotated(
|
|||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
w2: number,
|
||||
h2: number,
|
||||
x2?: number,
|
||||
y2?: number,
|
||||
w2?: number,
|
||||
h2?: number,
|
||||
) {
|
||||
if (typeof h2 !== "undefined") {
|
||||
if (typeof x2 !== "undefined" && typeof h2 !== "undefined" && typeof w2 !== "undefined" && typeof y2 !== "undefined") {
|
||||
ctx.translate(x2, y2);
|
||||
ctx.rotate(angle);
|
||||
ctx.drawImage(img, x, y, w, h, -w2 / 2, -h2 / 2, w2, h2);
|
||||
|
|
|
|||
|
|
@ -36,9 +36,13 @@ const config = {
|
|||
use: [stylesHandler, "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
|
||||
test: /\.(eot|svg|ttf|woff|woff2|jpg|gif)$/i,
|
||||
type: "asset",
|
||||
},
|
||||
{
|
||||
test: /\.(png)$/i,
|
||||
type: "asset/inline",
|
||||
},
|
||||
|
||||
// Add your rules for custom modules here
|
||||
// Learn more about loaders from https://webpack.js.org/loaders/
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user