Skip to content

Commit f1927ba

Browse files
authored
Merge pull request #599 from Gatoc-Idk/patch-4
Create rpg collisions.js
2 parents 0e934ea + b08f990 commit f1927ba

4 files changed

Lines changed: 350 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## Warning; i only wrote here info about blocks i thought needed more explanation
2+
3+
# TAGS:
4+
Tags change how some sprites work for example
5+
sprites tagged with "wall" will be obstacles for players
6+
players will avoid walls
7+
and decoration/ui will umm just be there and not avoid walls
8+
9+
# ENABLE/DISABLE Blocks
10+
When enabled a sprite will interact with the rpg collisions,
11+
when disabled they will not interact and completely ignore everything passing trough walls
12+
13+
# MOVING
14+
Move 10 steps avoiding walls; This block just moves 10 steps and when
15+
it touches a wall it stops it from passing trough
16+
Move 10 steps in direction (90) avoiding walls; does the same as last one but in a diferemt direction
17+
Move 10 steps in towards x;() y;() avoiding walls; does the same as the first one but looking towards a coordinate
18+
push out of any wall im stuck in; if inside a wall sprite it teleports outside of it
19+
20+
# WALL DETECTING
21+
When i touch a wall; detects when the sprite selected is touching a wall sprite
22+
Would touch wall at x;() y;(); Detects if the sprite selected WOULD touch a wall sprite in a certain coordinate

src/lib/extensions.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,4 +670,14 @@ export default [
670670
tags: ["customtype","data","utility","new","large"],
671671
creatorAlias: "AndrewGaming587"
672672
},
673+
{
674+
name: "RPG Collisions",
675+
description: "Make sprites avoid walking trough walls and more!",
676+
code: "Gatoc_Dev/rpg collisions.js",
677+
banner: "Gatoc_Dev/IMG_6103.jpeg",
678+
creator: "Gatoc_Dev",
679+
isGitHub: true,
680+
tags: ["rpg","collisions","utility","new","large","2D"],
681+
creatorAlias: "GatocDev"
682+
},
673683
];
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
// Name: RPG Collisions
2+
// ID: rpgcollisions
3+
// Description: Tag sprites as Wall, Player, or None and get automatic RPG-style solid collisions — no more walking through walls.
4+
// By: Gatoc_Dev
5+
// License: MIT
6+
7+
(function (Scratch) {
8+
'use strict';
9+
10+
if (!Scratch.extensions.unsandboxed) {
11+
throw new Error('RPG Collisions must run unsandboxed');
12+
}
13+
14+
const vm = Scratch.vm;
15+
const runtime = vm.runtime;
16+
17+
const TAG_WALL = 'wall';
18+
const TAG_PLAYER = 'player';
19+
const TAG_NONE = 'none';
20+
21+
const ICON = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj4KPHJlY3QgeD0iMSIgeT0iMSIgd2lkdGg9IjM4IiBoZWlnaHQ9IjM4IiByeD0iOCIgZmlsbD0iIzRDQUY1MCIgc3Ryb2tlPSIjMkU3RDMyIiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iNyIgeT0iOSIgd2lkdGg9IjEwIiBoZWlnaHQ9IjYiIGZpbGw9IiNFOEY1RTkiIHN0cm9rZT0iIzJFN0QzMiIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxyZWN0IHg9IjE5IiB5PSI5IiB3aWR0aD0iMTAiIGhlaWdodD0iNiIgZmlsbD0iI0U4RjVFOSIgc3Ryb2tlPSIjMkU3RDMyIiBzdHJva2Utd2lkdGg9IjEiLz4KPHJlY3QgeD0iMTMiIHk9IjE3IiB3aWR0aD0iMTAiIGhlaWdodD0iNiIgZmlsbD0iI0U4RjVFOSIgc3Ryb2tlPSIjMkU3RDMyIiBzdHJva2Utd2lkdGg9IjEiLz4KPHJlY3QgeD0iMjUiIHk9IjE3IiB3aWR0aD0iOCIgaGVpZ2h0PSI2IiBmaWxsPSIjRThGNUU5IiBzdHJva2U9IiMyRTdEMzIiIHN0cm9rZS13aWR0aD0iMSIvPgo8cmVjdCB4PSI3IiB5PSIxNyIgd2lkdGg9IjQiIGhlaWdodD0iNiIgZmlsbD0iI0U4RjVFOSIgc3Ryb2tlPSIjMkU3RDMyIiBzdHJva2Utd2lkdGg9IjEiLz4KPHJlY3QgeD0iNyIgeT0iMjUiIHdpZHRoPSIxMCIgaGVpZ2h0PSI2IiBmaWxsPSIjRThGNUU5IiBzdHJva2U9IiMyRTdEMzIiIHN0cm9rZS13aWR0aD0iMSIvPgo8cmVjdCB4PSIxOSIgeT0iMjUiIHdpZHRoPSIxMCIgaGVpZ2h0PSI2IiBmaWxsPSIjRThGNUU5IiBzdHJva2U9IiMyRTdEMzIiIHN0cm9rZS13aWR0aD0iMSIvPgo8Y2lyY2xlIGN4PSIzMCIgY3k9IjMwIiByPSI0LjUiIGZpbGw9IiMxQjVFMjAiLz4KPC9zdmc+Cg==';
22+
23+
class RPGCollisions {
24+
constructor() {
25+
this.MICRO_STEP = 2;
26+
}
27+
28+
getInfo() {
29+
return {
30+
id: 'rpgcollisions',
31+
name: 'RPG Collisions',
32+
color1: '#4CAF50',
33+
color2: '#43A047',
34+
color3: '#2E7D32',
35+
blockIconURI: ICON,
36+
menuIconURI: ICON,
37+
blocks: [
38+
{
39+
opcode: 'setTag',
40+
blockType: Scratch.BlockType.COMMAND,
41+
text: 'tag this sprite as [TAG]',
42+
arguments: {
43+
TAG: { type: Scratch.ArgumentType.STRING, menu: 'tags', defaultValue: TAG_WALL }
44+
}
45+
},
46+
{
47+
opcode: 'getTag',
48+
blockType: Scratch.BlockType.REPORTER,
49+
text: 'tag of this sprite'
50+
},
51+
{
52+
opcode: 'isSpriteTagged',
53+
blockType: Scratch.BlockType.BOOLEAN,
54+
text: '[SPRITE] is tagged as [TAG] ?',
55+
arguments: {
56+
SPRITE: { type: Scratch.ArgumentType.STRING, menu: 'spriteMenu' },
57+
TAG: { type: Scratch.ArgumentType.STRING, menu: 'tags', defaultValue: TAG_WALL }
58+
}
59+
},
60+
{
61+
opcode: 'enableCollisions',
62+
blockType: Scratch.BlockType.COMMAND,
63+
text: 'enable this sprite from rpg collisions'
64+
},
65+
{
66+
opcode: 'disableCollisions',
67+
blockType: Scratch.BlockType.COMMAND,
68+
text: 'disable this sprite from rpg collisions'
69+
},
70+
{
71+
opcode: 'collisionsEnabled',
72+
blockType: Scratch.BlockType.BOOLEAN,
73+
text: 'rpg collisions enabled for this sprite?'
74+
},
75+
'---',
76+
{
77+
opcode: 'moveAvoidingWalls',
78+
blockType: Scratch.BlockType.COMMAND,
79+
text: 'move [STEPS] steps avoiding walls',
80+
arguments: {
81+
STEPS: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 }
82+
}
83+
},
84+
{
85+
opcode: 'stepInDirection',
86+
blockType: Scratch.BlockType.COMMAND,
87+
text: 'step [STEPS] steps in direction [DIR] avoiding walls',
88+
arguments: {
89+
STEPS: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 },
90+
DIR: { type: Scratch.ArgumentType.ANGLE, defaultValue: 90 }
91+
}
92+
},
93+
{
94+
opcode: 'moveTowardsAvoidingWalls',
95+
blockType: Scratch.BlockType.COMMAND,
96+
text: 'move up to [STEPS] steps towards x: [X] y: [Y] avoiding walls',
97+
arguments: {
98+
STEPS: { type: Scratch.ArgumentType.NUMBER, defaultValue: 10 },
99+
X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 },
100+
Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }
101+
}
102+
},
103+
{
104+
opcode: 'pushOutOfWalls',
105+
blockType: Scratch.BlockType.COMMAND,
106+
text: 'push out of any wall I\'m stuck in'
107+
},
108+
'---',
109+
{
110+
opcode: 'touchingWall',
111+
blockType: Scratch.BlockType.BOOLEAN,
112+
text: 'touching a wall?'
113+
},
114+
{
115+
opcode: 'wouldTouchWallAt',
116+
blockType: Scratch.BlockType.BOOLEAN,
117+
text: 'would touch a wall at x: [X] y: [Y] ?',
118+
arguments: {
119+
X: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 },
120+
Y: { type: Scratch.ArgumentType.NUMBER, defaultValue: 0 }
121+
}
122+
},
123+
{
124+
opcode: 'whenTouchingWall',
125+
blockType: Scratch.BlockType.HAT,
126+
text: 'when I touch a wall',
127+
isEdgeActivated: true
128+
}
129+
],
130+
menus: {
131+
tags: {
132+
acceptReporters: false,
133+
items: [
134+
{ text: 'wall', value: TAG_WALL },
135+
{ text: 'player', value: TAG_PLAYER },
136+
{ text: 'none (decoration/UI)', value: TAG_NONE }
137+
]
138+
},
139+
spriteMenu: {
140+
acceptReporters: true,
141+
items: 'getSpriteMenu'
142+
}
143+
}
144+
};
145+
}
146+
147+
// ---------- menu helpers ----------
148+
149+
getSpriteMenu() {
150+
const names = runtime.targets
151+
.filter(t => t.isOriginal && !t.isStage)
152+
.map(t => t.getName());
153+
return names.length ? names : [''];
154+
}
155+
156+
157+
getTagOf(target) {
158+
if (!target) return TAG_NONE;
159+
return target.rpgCollisionTag || TAG_NONE;
160+
}
161+
162+
setTag(args, util) {
163+
const tag = String(args.TAG);
164+
util.target.rpgCollisionTag = (tag === TAG_WALL || tag === TAG_PLAYER) ? tag : TAG_NONE;
165+
}
166+
167+
getTag(args, util) {
168+
return this.getTagOf(util.target);
169+
}
170+
171+
isSpriteTagged(args) {
172+
const spriteName = String(args.SPRITE);
173+
const target = runtime.getSpriteTargetByName(spriteName);
174+
return this.getTagOf(target) === String(args.TAG);
175+
}
176+
.
177+
178+
isEnabledTarget(target) {
179+
if (!target) return false;
180+
return target.rpgCollisionEnabled !== false;
181+
}
182+
183+
enableCollisions(args, util) {
184+
util.target.rpgCollisionEnabled = true;
185+
}
186+
187+
disableCollisions(args, util) {
188+
util.target.rpgCollisionEnabled = false;
189+
}
190+
191+
collisionsEnabled(args, util) {
192+
return this.isEnabledTarget(util.target);
193+
}
194+
195+
196+
197+
getWallDrawableIDs(excludeTarget) {
198+
const ids = [];
199+
for (const t of runtime.targets) {
200+
if (t === excludeTarget) continue;
201+
if (t.isStage) continue;
202+
if (this.getTagOf(t) === TAG_WALL && this.isEnabledTarget(t)) {
203+
ids.push(t.drawableID);
204+
}
205+
}
206+
return ids;
207+
}
208+
209+
isTouchingWalls(target) {
210+
if (!runtime.renderer) return false;
211+
if (!this.isEnabledTarget(target)) return false;
212+
const wallIDs = this.getWallDrawableIDs(target);
213+
if (wallIDs.length === 0) return false;
214+
return runtime.renderer.isTouchingDrawables(target.drawableID, wallIDs);
215+
}
216+
217+
218+
slideMove(target, dxUnit, dyUnit, distance) {
219+
const step = this.MICRO_STEP;
220+
let moved = 0;
221+
const total = Math.abs(distance);
222+
const sign = distance < 0 ? -1 : 1;
223+
224+
while (moved < total) {
225+
const thisStep = Math.min(step, total - moved);
226+
const oldX = target.x;
227+
const oldY = target.y;
228+
const nx = oldX + dxUnit * thisStep * sign;
229+
const ny = oldY + dyUnit * thisStep * sign;
230+
target.setXY(nx, ny);
231+
if (this.isTouchingWalls(target)) {
232+
target.setXY(oldX, oldY);
233+
break;
234+
}
235+
moved += thisStep;
236+
}
237+
return moved;
238+
}
239+
240+
241+
242+
moveAvoidingWalls(args, util) {
243+
const target = util.target;
244+
const steps = Number(args.STEPS) || 0;
245+
const radians = (90 - target.direction) * Math.PI / 180;
246+
const dx = Math.cos(radians);
247+
const dy = Math.sin(radians);
248+
this.slideMove(target, dx, dy, steps);
249+
}
250+
251+
stepInDirection(args, util) {
252+
const target = util.target;
253+
const steps = Number(args.STEPS) || 0;
254+
const dir = Number(args.DIR) || 0;
255+
const radians = (90 - dir) * Math.PI / 180;
256+
const dx = Math.cos(radians);
257+
const dy = Math.sin(radians);
258+
this.slideMove(target, dx, dy, steps);
259+
}
260+
261+
moveTowardsAvoidingWalls(args, util) {
262+
const target = util.target;
263+
const targetX = Number(args.X) || 0;
264+
const targetY = Number(args.Y) || 0;
265+
const diffX = targetX - target.x;
266+
const diffY = targetY - target.y;
267+
const distToPoint = Math.sqrt(diffX * diffX + diffY * diffY);
268+
if (distToPoint < 0.001) return;
269+
270+
const dxUnit = diffX / distToPoint;
271+
const dyUnit = diffY / distToPoint;
272+
const steps = Math.min(Number(args.STEPS) || 0, distToPoint);
273+
this.slideMove(target, dxUnit, dyUnit, steps);
274+
}
275+
276+
pushOutOfWalls(args, util) {
277+
const target = util.target;
278+
if (!this.isTouchingWalls(target)) return;
279+
280+
.
281+
const maxRadius = 64;
282+
for (let radius = this.MICRO_STEP; radius <= maxRadius; radius += this.MICRO_STEP) {
283+
for (let angle = 0; angle < 360; angle += 15) {
284+
const radians = angle * Math.PI / 180;
285+
const nx = target.x + Math.cos(radians) * radius;
286+
const ny = target.y + Math.sin(radians) * radius;
287+
const oldX = target.x;
288+
const oldY = target.y;
289+
target.setXY(nx, ny);
290+
if (!this.isTouchingWalls(target)) return;
291+
target.setXY(oldX, oldY);
292+
}
293+
}
294+
}
295+
296+
297+
298+
touchingWall(args, util) {
299+
return this.isTouchingWalls(util.target);
300+
}
301+
302+
wouldTouchWallAt(args, util) {
303+
const target = util.target;
304+
const oldX = target.x;
305+
const oldY = target.y;
306+
target.setXY(Number(args.X) || 0, Number(args.Y) || 0);
307+
const result = this.isTouchingWalls(target);
308+
target.setXY(oldX, oldY);
309+
return result;
310+
}
311+
312+
whenTouchingWall(args, util) {
313+
return this.isTouchingWalls(util.target);
314+
}
315+
}
316+
317+
Scratch.extensions.register(new RPGCollisions());
318+
})(Scratch);
21.5 KB
Loading

0 commit comments

Comments
 (0)