探索Processing中的碰撞检测与鼠标键盘交互
碰撞检测
在处理一些模拟场景时,比如多个物体在窗口中移动,会遇到物体相互重叠的问题。以多个变形虫在窗口中移动为例,为了防止它们相互重叠,需要进行碰撞检测。
由于变形虫大致呈圆形,所以可以使用圆 - 圆碰撞检测算法。该算法的原理是:对于两个圆,如果它们圆心之间的距离大于两圆半径之和,那么这两个圆没有碰撞;反之,如果圆心距离小于两圆半径之和,则两圆发生了碰撞。
# 伪代码示例,用于检测碰撞
for a in amoebas:
for b in amoebas:
if a is b:
continue
# 此处添加具体的碰撞检测和处理代码
在上述代码中,使用两层循环遍历所有变形虫对。
if a is b
语句用于避免检测一个变形虫与自身的碰撞,若
a
和
b
是同一个实例,就跳过当前循环。
可以思考如何利用距离向量将碰撞的变形虫推开,例如添加(或减去)距离向量的一部分,使变形虫朝与碰撞对象相反的方向移动。
面向对象编程与模块化
通过面向对象编程,可以在Python中对现实世界的对象进行建模。定义一个新的类,如
Amoeba
类,并为其添加属性和方法。类就像一个对象模板,可以创建无数个实例。将相关的变量(属性)和函数(方法)组合到类中,能更有效地组织代码,这在编写大型、复杂项目时尤为有效。
同时,还可以将类和其他代码分离到不同的Python文件(模块)中,以便在项目之间共享代码,或者在同一项目的不同文件中作为可重用组件。模块可以减少主代码的行数,让我们专注于更高级的逻辑。
Processing中的向量类
Processing提供了内置的
PVector
类来处理欧几里得向量。欧几里得向量描述了既有大小又有方向的量,也可以用来存储物体的位置(以x - y坐标形式)。在模拟场景中,可以使用向量来模拟力并控制各种对象在显示窗口中的位置。
鼠标和键盘交互
在编程中,处理鼠标和键盘交互可以让程序更加生动和实用。可以将鼠标和键盘输入设备以有趣且有用的方式结合起来,例如在许多电脑游戏中,使用按键控制玩家移动,使用鼠标进行瞄准。
鼠标交互
鼠标输入可以用于执行点选操作,也可以编程实现结合鼠标移动和点击的手势动作,如拖放或平移。大多数鼠标有三个按钮:左键、右键和可点击的滚轮(兼作中键)。
Processing提供了一些系统变量来监控鼠标的状态:
-
mouseX
和
mouseY
:分别表示鼠标指针在显示窗口中的水平和垂直位置。
-
pmouseX
和
pmouseY
:包含上一帧的鼠标坐标。
-
mousePressed
:当鼠标按钮被按下时,该变量被设置为
True
。
下面是一个使用这些变量创建简单刮画程序的示例:
def setup():
size(800, 400)
frameRate(20)
background('#000000')
stroke('#FFFFFF')
def draw():
circle(mouseX, mouseY, 15)
在上述代码中,每帧Processing会根据
mouseX
和
mouseY
的值绘制一个新的白色圆圈。由于
draw()
块中没有
background()
函数,所以绘制的圆圈会一直保留,直到关闭显示窗口。
如果想要绘制连续的线条,可以将
circle()
函数替换为
line()
函数:
def draw():
strokeWeight(15)
line(mouseX, mouseY, pmouseX, pmouseY)
为了实现只有在按下鼠标左键时才绘制线条的功能,可以添加
if
语句:
def draw():
strokeWeight(15)
if mousePressed and mouseButton == LEFT:
line(mouseX, mouseY, pmouseX, pmouseY)
为了实现彩虹色刮画效果,可以根据鼠标指针的位置设置线条的颜色:
def draw():
colorMode(HSB, 360, 100, 100)
h = mouseX * 360.0 / width
s = mouseY * 100.0 / height
b = 100
stroke(h, s, b)
strokeWeight(15)
if mousePressed and mouseButton == LEFT:
line(mouseX, mouseY, pmouseX, pmouseY)
鼠标事件
Processing提供了一系列鼠标事件函数,每次特定的鼠标事件发生时,这些函数就会执行。这些函数包括
mouseClicked()
、
mouseDragged()
、
mouseMoved()
、
mousePressed()
、
mouseReleased()
和
mouseWheel()
。
下面是使用
mousePressed
变量和
mousePressed()
事件函数的对比示例:
# 使用 mousePressed 变量
def draw():
background('#FF0000') # 红色
if mousePressed:
background('#0000FF') # 蓝色
# 使用 mousePressed() 事件函数
def draw():
background('#FF0000') # 红色
def mousePressed():
background('#0000FF') # 蓝色
在第一个示例中,只要用户按下鼠标按钮,背景颜色就会变为蓝色;在第二个示例中,每次按下鼠标按钮时,显示窗口会短暂闪烁蓝色(仅一帧),然后立即恢复为红色,因为事件函数每次事件只执行一次。
创建绘画应用
接下来,将创建一个基本的绘画应用,该应用具有一个工具调色板,用于选择颜色样本和其他选项。会使用
mousePressed()
、
mouseReleased()
和
mouseWheel()
函数。
准备工作
-
创建一个新的草图并保存为
paint_app。 -
从指定的GitHub网页下载
Ernest.ttf字体文件,并将其放在草图文件夹的新data子文件夹中。
def setup():
size(600, 600)
background('#004477')
ernest = createFont('Ernest.ttf', 20)
textFont(ernest)
swatches = ['#FF0000', '#FF9900', '#FFFF00',
'#00FF00', '#0099FF', '#6633FF']
brushcolor = swatches[2]
brushshape = ROUND
brushsize = 3
painting = False
paintmode = 'free'
palette = 60
在上述代码中,定义了显示窗口的大小、背景颜色、字体和一些全局变量,用于调整和监控画笔的状态。默认画笔颜色设置为黄色。
控制绘制循环
使用
loop()
和
noLoop()
函数来控制
draw()
函数的行为。
noLoop()
函数会停止Processing持续执行
draw()
块中的代码,
loop()
函数会重新激活标准的
draw()
函数行为,
redraw()
函数可以用于仅执行一次
draw()
代码。
def setup():
size(600, 600)
background('#004477')
ernest = createFont('Ernest.ttf', 20)
textFont(ernest)
noLoop()
def draw():
print(frameCount)
global painting, paintmode
if paintmode == 'free':
if painting:
stroke(brushcolor)
strokeCap(brushshape)
strokeWeight(brushsize)
line(mouseX, mouseY, pmouseX, pmouseY)
elif frameCount > 1:
painting = True
def mousePressed():
# 开始绘画
if mouseButton == LEFT:
loop()
def mouseReleased():
# 停止绘画
if mouseButton == LEFT:
global painting
painting = False
noLoop()
在上述代码中,初始时
draw()
函数不循环。按下鼠标左键时,
loop()
函数使
draw()
函数开始循环;释放鼠标左键时,
noLoop()
函数停止循环。
添加可选颜色样本
在工具调色板中添加六个颜色样本,用户可以点击这些样本更改画笔颜色。
def draw():
# ...之前的代码...
# 黑色面板
noStroke()
fill('#000000')
rect(0, 0, palette, height)
# 颜色样本
for i, swatch in enumerate(swatches):
sx = int(i%2) * palette/2
sy = int(i/2) * palette/2
fill(swatch)
square(sx, sy, palette/2)
def mousePressed():
# ...之前的代码...
# 样本选择
if mouseButton == LEFT and mouseX < palette and mouseY < 90:
global brushcolor
brushcolor = get(mouseX, mouseY)
在上述代码中,使用
for
循环遍历
swatches
列表,绘制一个由不同颜色填充的正方形网格。当用户点击颜色样本时,使用
get()
函数获取鼠标指针下像素的颜色,并将其赋值给
brushcolor
变量。
使用鼠标滚轮调整画笔大小
mouseWheel()
事件函数用于在鼠标滚轮移动时执行代码,并可以根据滚轮的旋转方向获取正或负的值。
def draw():
# ...之前的代码...
# 画笔预览
fill(brushcolor)
if brushshape == ROUND:
circle(palette/2, 123, brushsize)
paintmode = 'free'
def mouseWheel(e):
# 调整画笔大小
global brushsize, paintmode
paintmode = 'select'
brushsize += e.count
if brushsize < 3:
brushsize = 3
if brushsize > 45:
brushsize = 45
redraw()
在上述代码中,当鼠标滚轮移动时,
mouseWheel()
函数会根据滚轮的旋转方向调整画笔大小,并确保画笔大小在合理范围内。同时,为了避免在调整画笔大小时绘画,将
paintmode
切换为
select
。
为了解决使用大画笔选择颜色样本时,颜料可能延伸到画布区域的问题,可以在
draw()
函数中添加一个
if
语句:
def draw():
print(frameCount)
global painting, paintmode
if mouseX < palette:
paintmode = 'select'
# ...之前的代码...
通过上述步骤,就可以创建一个功能较为丰富的绘画应用,并且可以根据需要进一步扩展其功能。
下面是一个简单的流程图,展示绘画应用的主要流程:
graph TD;
A[开始] --> B[初始化设置];
B --> C[等待鼠标事件];
C --> D{鼠标左键按下};
D -- 是 --> E[开始绘画循环];
D -- 否 --> C;
E --> F{鼠标左键释放};
F -- 是 --> G[停止绘画循环];
F -- 否 --> H{鼠标滚轮滚动};
H -- 是 --> I[调整画笔大小];
H -- 否 --> J{鼠标点击颜色样本};
J -- 是 --> K[更改画笔颜色];
J -- 否 --> E;
G --> C;
通过以上内容,我们学习了如何进行碰撞检测、使用面向对象编程和模块化、处理鼠标和键盘交互,以及创建一个基本的绘画应用。这些知识可以帮助我们开发出更加丰富和交互性强的程序。
探索Processing中的碰撞检测与鼠标键盘交互
碰撞检测的深入思考
在处理碰撞检测时,除了检测碰撞的发生,还需要考虑如何处理碰撞后的情况。对于变形虫的例子,当检测到碰撞后,可以通过向量运算来推开碰撞的变形虫。例如,可以计算两个变形虫中心之间的距离向量,然后将这个向量的一部分加到其中一个变形虫的位置上,同时从另一个变形虫的位置上减去相同的部分,从而实现推开的效果。
以下是一个简单的伪代码示例:
for a in amoebas:
for b in amoebas:
if a is b:
continue
# 计算两个变形虫中心的距离
distance = calculate_distance(a.center, b.center)
if distance < a.radius + b.radius:
# 发生碰撞,计算推开向量
push_vector = calculate_push_vector(a.center, b.center)
# 推开变形虫
a.position += push_vector * 0.1
b.position -= push_vector * 0.1
鼠标和键盘交互的更多应用场景
除了前面提到的刮画程序和绘画应用,鼠标和键盘交互还可以应用于更多场景。
游戏开发
在游戏开发中,鼠标和键盘交互是非常重要的。例如,在一个射击游戏中,可以使用鼠标控制瞄准方向,使用键盘控制角色的移动。可以通过监听鼠标的点击事件来触发射击动作,监听键盘的按键事件来控制角色的前后左右移动。
def draw():
# 绘制游戏场景
draw_game_scene()
def mousePressed():
if mouseButton == LEFT:
# 触发射击动作
shoot()
def keyPressed():
if key == 'W':
# 向前移动
move_forward()
elif key == 'S':
# 向后移动
move_backward()
elif key == 'A':
# 向左移动
move_left()
elif key == 'D':
# 向右移动
move_right()
数据可视化
在数据可视化中,鼠标和键盘交互可以让用户更方便地探索数据。例如,在一个柱状图可视化中,可以使用鼠标悬停在柱子上显示详细的数据信息,使用键盘的上下左右箭头键来切换不同的数据视图。
def draw():
# 绘制柱状图
draw_bar_chart()
def mouseMoved():
for bar in bars:
if is_mouse_over(bar):
# 鼠标悬停在柱子上,显示详细信息
show_details(bar)
def keyPressed():
if key == CODED:
if keyCode == UP:
# 切换到上一个数据视图
switch_view('up')
elif keyCode == DOWN:
# 切换到下一个数据视图
switch_view('down')
elif keyCode == LEFT:
# 切换到左一个数据视图
switch_view('left')
elif keyCode == RIGHT:
# 切换到右一个数据视图
switch_view('right')
挑战与扩展
挑战 #11:添加绘画应用功能
可以为前面创建的绘画应用添加更多功能,例如添加更多的画笔形状、添加撤销和重做功能、添加保存绘画的功能等。
以下是一个添加撤销和重做功能的简单示例:
# 初始化撤销和重做栈
undo_stack = []
redo_stack = []
def draw():
# ...之前的代码...
if keyPressed and key == 'Z':
if len(undo_stack) > 0:
# 撤销操作
redo_stack.append(current_state())
restore_state(undo_stack.pop())
elif keyPressed and key == 'Y':
if len(redo_stack) > 0:
# 重做操作
undo_stack.append(current_state())
restore_state(redo_stack.pop())
def mousePressed():
# ...之前的代码...
# 保存当前状态到撤销栈
undo_stack.append(current_state())
redo_stack = []
扩展:自定义鼠标指针
可以使用
cursor()
函数将鼠标指针从默认的箭头更改为其他形状。例如,将鼠标指针改为十字线:
def setup():
size(600, 600)
background('#004477')
ernest = createFont('Ernest.ttf', 20)
textFont(ernest)
cursor(CROSS)
总结
通过本文的学习,我们掌握了多个重要的编程概念和技能。
| 概念/技能 | 描述 |
|---|---|
| 碰撞检测 | 学会使用圆 - 圆碰撞检测算法,检测物体之间的碰撞,并思考如何处理碰撞后的情况 |
| 面向对象编程与模块化 | 了解如何使用类来建模现实世界的对象,将相关的变量和函数组合到类中,以及如何将代码分离到不同的模块中 |
| 鼠标和键盘交互 | 掌握了Processing中用于监控鼠标和键盘状态的系统变量和事件函数,能够创建具有交互性的程序,如刮画程序和绘画应用 |
| 绘画应用开发 | 学会创建一个基本的绘画应用,包括工具调色板、颜色选择、画笔大小调整等功能,并可以根据需求进一步扩展其功能 |
同时,我们还探讨了鼠标和键盘交互在游戏开发和数据可视化等领域的应用,以及如何应对挑战和扩展程序功能。这些知识和技能将为我们开发更加丰富和交互性强的程序奠定坚实的基础。
下面是一个总结性的流程图,展示从学习基础概念到创建应用并进行扩展的整个过程:
graph TD;
A[学习基础概念] --> B[碰撞检测];
A --> C[面向对象编程与模块化];
A --> D[鼠标和键盘交互];
B --> E[创建绘画应用];
C --> E;
D --> E;
E --> F[应用扩展];
F --> G[添加更多功能];
F --> H[自定义鼠标指针];
F --> I[应用于其他领域];
希望通过本文的学习,你能够在编程的道路上更进一步,开发出更多有趣和实用的程序。
超级会员免费看
47

被折叠的 条评论
为什么被折叠?



