Skip to content

Commit a26cf40

Browse files
authored
Merge pull request #218 from scratchcpp/motion_blocks1
Implement motion blocks (part 1)
2 parents 1ef8d42 + 191d617 commit a26cf40

File tree

4 files changed

+753
-0
lines changed

4 files changed

+753
-0
lines changed

src/blocks/motionblocks.cpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,227 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <scratchcpp/iengine.h>
4+
#include <scratchcpp/compiler.h>
5+
#include <scratchcpp/sprite.h>
6+
#include <scratchcpp/input.h>
7+
38
#include "motionblocks.h"
9+
#include "../engine/internal/randomgenerator.h"
410

511
using namespace libscratchcpp;
612

13+
static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20
14+
15+
IRandomGenerator *MotionBlocks::rng = nullptr;
16+
717
std::string MotionBlocks::name() const
818
{
919
return "Motion";
1020
}
1121

1222
void MotionBlocks::registerBlocks(IEngine *engine)
1323
{
24+
// Blocks
25+
engine->addCompileFunction(this, "motion_movesteps", &compileMoveSteps);
26+
engine->addCompileFunction(this, "motion_turnright", &compileTurnRight);
27+
engine->addCompileFunction(this, "motion_turnleft", &compileTurnLeft);
28+
engine->addCompileFunction(this, "motion_pointindirection", &compilePointInDirection);
29+
engine->addCompileFunction(this, "motion_pointtowards", &compilePointTowards);
30+
engine->addCompileFunction(this, "motion_gotoxy", &compileGoToXY);
31+
32+
// Inputs
33+
engine->addInput(this, "STEPS", STEPS);
34+
engine->addInput(this, "DEGREES", DEGREES);
35+
engine->addInput(this, "DIRECTION", DIRECTION);
36+
engine->addInput(this, "TOWARDS", TOWARDS);
37+
engine->addInput(this, "X", X);
38+
engine->addInput(this, "Y", Y);
39+
}
40+
41+
void MotionBlocks::compileMoveSteps(Compiler *compiler)
42+
{
43+
compiler->addInput(STEPS);
44+
compiler->addFunctionCall(&moveSteps);
45+
}
46+
47+
void MotionBlocks::compileTurnRight(Compiler *compiler)
48+
{
49+
compiler->addInput(DEGREES);
50+
compiler->addFunctionCall(&turnRight);
51+
}
52+
53+
void MotionBlocks::compileTurnLeft(Compiler *compiler)
54+
{
55+
compiler->addInput(DEGREES);
56+
compiler->addFunctionCall(&turnLeft);
57+
}
58+
59+
void MotionBlocks::compilePointInDirection(Compiler *compiler)
60+
{
61+
compiler->addInput(DIRECTION);
62+
compiler->addFunctionCall(&pointInDirection);
63+
}
64+
65+
void MotionBlocks::compilePointTowards(Compiler *compiler)
66+
{
67+
Input *input = compiler->input(TOWARDS);
68+
69+
if (input->type() != Input::Type::ObscuredShadow) {
70+
assert(input->pointsToDropdownMenu());
71+
std::string value = input->selectedMenuItem();
72+
73+
if (value == "_mouse_")
74+
compiler->addFunctionCall(&pointTowardsMousePointer);
75+
else if (value == "_random_")
76+
compiler->addFunctionCall(&pointTowardsRandomPosition);
77+
else {
78+
int index = compiler->engine()->findTarget(value);
79+
compiler->addConstValue(index);
80+
compiler->addFunctionCall(&pointTowardsByIndex);
81+
}
82+
} else {
83+
compiler->addInput(input);
84+
compiler->addFunctionCall(&pointTowards);
85+
}
86+
}
87+
88+
void MotionBlocks::compileGoToXY(Compiler *compiler)
89+
{
90+
compiler->addInput(X);
91+
compiler->addInput(Y);
92+
compiler->addFunctionCall(&goToXY);
93+
}
94+
95+
unsigned int MotionBlocks::moveSteps(VirtualMachine *vm)
96+
{
97+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
98+
99+
if (sprite) {
100+
double dir = sprite->direction();
101+
double steps = vm->getInput(0, 1)->toDouble();
102+
sprite->setX(sprite->x() + std::sin(dir * pi / 180) * steps);
103+
sprite->setY(sprite->y() + std::cos(dir * pi / 180) * steps);
104+
}
105+
106+
return 1;
107+
}
108+
109+
unsigned int MotionBlocks::turnRight(VirtualMachine *vm)
110+
{
111+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
112+
113+
if (sprite)
114+
sprite->setDirection(sprite->direction() + vm->getInput(0, 1)->toDouble());
115+
116+
return 1;
117+
}
118+
119+
unsigned int MotionBlocks::turnLeft(VirtualMachine *vm)
120+
{
121+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
122+
123+
if (sprite)
124+
sprite->setDirection(sprite->direction() - vm->getInput(0, 1)->toDouble());
125+
126+
return 1;
127+
}
128+
129+
unsigned int MotionBlocks::pointInDirection(VirtualMachine *vm)
130+
{
131+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
132+
133+
if (sprite)
134+
sprite->setDirection(vm->getInput(0, 1)->toDouble());
135+
136+
return 1;
137+
}
138+
139+
void MotionBlocks::pointTowardsPos(Sprite *sprite, double x, double y)
140+
{
141+
if (!sprite)
142+
return;
143+
144+
// https://en.scratch-wiki.info/wiki/Point_Towards_()_(block)#Workaround
145+
double deltaX = x - sprite->x();
146+
double deltaY = y - sprite->y();
147+
148+
if (deltaY == 0) {
149+
if (deltaX < 0)
150+
sprite->setDirection(-90);
151+
else
152+
sprite->setDirection(90);
153+
} else if (deltaY < 0)
154+
sprite->setDirection(180 + (180 / pi) * std::atan(deltaX / deltaY));
155+
else
156+
sprite->setDirection((180 / pi) * std::atan(deltaX / deltaY));
157+
}
158+
159+
unsigned int MotionBlocks::pointTowards(VirtualMachine *vm)
160+
{
161+
std::string value = vm->getInput(0, 1)->toString();
162+
Target *target;
163+
164+
if (value == "_mouse_")
165+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY());
166+
else if (value == "_random_") {
167+
// TODO: Read stage size from engine (#224)
168+
static const unsigned int stageWidth = 480;
169+
static const unsigned int stageHeight = 360;
170+
171+
if (!rng)
172+
rng = RandomGenerator::instance().get();
173+
174+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2));
175+
} else {
176+
target = vm->engine()->targetAt(vm->engine()->findTarget(value));
177+
Sprite *sprite = dynamic_cast<Sprite *>(target);
178+
179+
if (sprite)
180+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), sprite->x(), sprite->y());
181+
}
182+
183+
return 1;
184+
}
185+
186+
unsigned int MotionBlocks::pointTowardsByIndex(VirtualMachine *vm)
187+
{
188+
Target *target = vm->engine()->targetAt(vm->getInput(0, 1)->toInt());
189+
Sprite *sprite = dynamic_cast<Sprite *>(target);
190+
191+
if (sprite)
192+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), sprite->x(), sprite->y());
193+
194+
return 1;
195+
}
196+
197+
unsigned int MotionBlocks::pointTowardsMousePointer(VirtualMachine *vm)
198+
{
199+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), vm->engine()->mouseX(), vm->engine()->mouseY());
200+
return 0;
201+
}
202+
203+
unsigned int MotionBlocks::pointTowardsRandomPosition(VirtualMachine *vm)
204+
{
205+
// TODO: Read stage size from engine (#224)
206+
static const unsigned int stageWidth = 480;
207+
static const unsigned int stageHeight = 360;
208+
209+
if (!rng)
210+
rng = RandomGenerator::instance().get();
211+
212+
pointTowardsPos(dynamic_cast<Sprite *>(vm->target()), rng->randint(-static_cast<int>(stageWidth / 2), stageWidth / 2), rng->randint(-static_cast<int>(stageHeight / 2), stageHeight / 2));
213+
214+
return 0;
215+
}
216+
217+
unsigned int MotionBlocks::goToXY(VirtualMachine *vm)
218+
{
219+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
220+
221+
if (sprite) {
222+
sprite->setX(vm->getInput(0, 2)->toDouble());
223+
sprite->setY(vm->getInput(1, 2)->toDouble());
224+
}
225+
226+
return 2;
14227
}

src/blocks/motionblocks.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,57 @@
77
namespace libscratchcpp
88
{
99

10+
class Sprite;
11+
class IRandomGenerator;
12+
1013
/*! \brief The MotionBlocks class contains the implementation of motion blocks. */
1114
class MotionBlocks : public IBlockSection
1215
{
1316
public:
17+
enum Inputs
18+
{
19+
STEPS,
20+
DEGREES,
21+
DIRECTION,
22+
TOWARDS,
23+
X,
24+
Y
25+
};
26+
27+
enum Fields
28+
{
29+
};
30+
31+
enum FieldValues
32+
{
33+
};
34+
1435
std::string name() const override;
1536

1637
void registerBlocks(IEngine *engine) override;
38+
39+
static void compileMoveSteps(Compiler *compiler);
40+
static void compileTurnRight(Compiler *compiler);
41+
static void compileTurnLeft(Compiler *compiler);
42+
static void compilePointInDirection(Compiler *compiler);
43+
static void compilePointTowards(Compiler *compiler);
44+
static void compileGoToXY(Compiler *compiler);
45+
46+
static unsigned int moveSteps(VirtualMachine *vm);
47+
static unsigned int turnRight(VirtualMachine *vm);
48+
static unsigned int turnLeft(VirtualMachine *vm);
49+
static unsigned int pointInDirection(VirtualMachine *vm);
50+
51+
static void pointTowardsPos(Sprite *sprite, double x, double y);
52+
53+
static unsigned int pointTowards(VirtualMachine *vm);
54+
static unsigned int pointTowardsByIndex(VirtualMachine *vm);
55+
static unsigned int pointTowardsMousePointer(VirtualMachine *vm);
56+
static unsigned int pointTowardsRandomPosition(VirtualMachine *vm);
57+
58+
static unsigned int goToXY(VirtualMachine *vm);
59+
60+
static IRandomGenerator *rng;
1761
};
1862

1963
} // namespace libscratchcpp

test/blocks/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,19 @@ target_link_libraries(
109109
)
110110

111111
gtest_discover_tests(sensing_blocks_test)
112+
113+
# motion_blocks_test
114+
add_executable(
115+
motion_blocks_test
116+
motion_blocks_test.cpp
117+
)
118+
119+
target_link_libraries(
120+
motion_blocks_test
121+
GTest::gtest_main
122+
GTest::gmock_main
123+
scratchcpp
124+
scratchcpp_mocks
125+
)
126+
127+
gtest_discover_tests(motion_blocks_test)

0 commit comments

Comments
 (0)