速安GeChe手游下载站_最新手机游戏下载门户网站!

游戏更新 | 安卓游戏 | 苹果游戏 | 推荐游戏 | 软件更新 | 文章更新 | 热门文章
您的位置: 首页  →  攻略 → 《字母俄罗斯方块(俄罗斯方块,原来每个方块都有名字)

字母俄罗斯方块(俄罗斯方块,原来每个方块都有名字)

2023-01-26 05:58:21      小编:网络整理      我要评论

字母俄罗斯方块文章列表:

字母俄罗斯方块(俄罗斯方块,原来每个方块都有名字)

俄罗斯方块,原来每个方块都有名字

大家好!我是乐同乐同

电子游戏是我们儿时美好的回忆,而《俄罗斯方块》,也是最容易勾起回忆,最经典的电子游戏。它不仅成为了游戏娱乐相对匮乏时代里不可替代的一种存在,也给全球那些曾经为之着迷过的人们留下了一段最美好的成长回忆。至今,《俄罗斯方块》在各个平台都有巨大的累积销量并成了众多休闲游戏难以超越的经典。而其中的每个方块也都有自己的“姓名”,玩过这么多年游戏的你知道吗?

《俄罗斯方块》是苏联科学家阿列克谢帕基特诺夫编写的,它最初的目的只是用于计算机程序检测,通俗点说,就是计算机自己玩的游戏。更富有想象力的是,他将自己酷爱的网球(Tennis),和希腊语的四(tetra)结合,创造了全新词汇“tetris” 俄罗斯方块。(好简单直接~)

不可能!为什么他的头发这么茂盛!他是假的程序员!

而伴随着全球的风靡,其中每个小方块的名字也被大家所熟知,它们除了有着正规字母学名之外,还被赋予了更具亲切的别名。玩家可以轻松从名字中,便能感受到方块的独特使用方法!如果你想成为方块大神,那么这些亲切的称呼一定要知道。

俄罗斯方块获得许多奖励,在多项评选中被评为最经典游戏之一。它还创造了一些世界纪录,比如它是在最多平台上运行的视频游戏,也是直接连累玩家坐牢时间最长的游戏(笑)。

这就是玩家的比赛

杩?0琛屼唬鐮佹嵁璇存槸鏈€鐗涢€肩殑淇勭綏鏂柟鍧椾唬鐮侊紒

浠g爜涓嶉渶瑕佸锛岀簿灏卞ソ銆傜湅浼肩畝绠€鍗曞崟鐨勪竴琛屼唬鐮侊紝鑳藉疄鐜伴潪甯稿己澶х殑鍔熻兘銆俉3Cschool灏忓笀濡逛负澶у鍒嗕韩60琛屼唬鐮侊紝鎹鏄渶鐗涢€肩殑淇勭綏鏂柟鍧椾唬鐮併€俓u003c/p>

绌剁珶鏄€庝箞鏍风殑浠g爜杩欎箞鐗涢€硷紝搴熻瘽涓嶅璇达紝鐩存帴涓哄ぇ瀹惰创涓婁唬鐮併€俓u003c/p>

1-20琛屼唬鐮乗u003cbr>

21-40琛屼唬鐮乗u003cbr>

41-60琛屼唬鐮乗u003cbr>

杩欐浠g爜鐪熺殑寰堝帀瀹冲悧?閭d箞缃戝弸鏄€庝箞璁や负鐨?

缃戝弸涓€锛氱湡缁欏姏!

缃戝弸浜岋細鐪熷己澶?

缃戝弸涓夛細鑶滄嫓涓憖!

淇勭綏鏂柟鍧梊u003cbr>

缃戝弸鍥涳細鍥惧舰鍙樺寲鐨勯敭鏄摢涓紝璨屼技娌¤繖涓姛鑳姐€俓u003c/p>

缃戝弸浜旓細璋佸啓鐨?杩欎箞鐗涢€?

鐪嬫潵澶у閮借寰楄繖60琛屼唬鐮佸緢鍘夊锛屽氨鏄病鏈変汉鏉ュ洖绛旓紝杩欎簺浠g爜绌剁珶鍘夊鍦ㄥ摢閲?涓嶇煡閬撳悇浣嶅ぇ渚犺寰楄繖浜涗唬鐮佹€庝箞鏍凤紝濡傛灉浣犵湅鎳備簡锛屽彲浠ュ湪涓嬮潰鐣欒█鍝?

瀛︾紪绋嬫妧鏈紝灏卞埌W3Cschool锛屽鏋滀綘鍠滄鎴戜滑鐨勬枃绔狅紝鍙互鐐瑰嚮鍙充笂瑙掑叧娉ㄦ垜浠?濡傛灉浣犳兂鐪嬪埌鏇村IT鐣岀殑璧勮锛屽彲浠ュ姞鎴戜滑鐨勫叕浼楀彿銆俓u003c/p>

鍏紬鍙凤細w3cschoolcn

Python大佬手把手教你用wxPython模块编写界面程序

wxPython是一个开发桌面端图形界面的跨平台函数库,开发语言为Python,它是基于C 的函数库wxWidgets的封装。

私信小编01即可获取Python学习资料

wxpython有大量组件,它们可以从逻辑上(注意是逻辑上)这样划分:

(1)基础组件

这些组件为其所派生的子组件提供基础功能,通常不直接使用。

(2)顶层组件

这些组件相互独立存在。

(3)容器

这些组件包含其他组件。

(4)动态组件

这些组件可以被用户所交互编辑。

(5)静态组件

这些组件用来展示信息,无法被用户所交互编辑。

(6)其他组件

这些组件包括状态栏、工具栏、菜单栏等。

除了逻辑上的划分,各个组件之间还存在着继承关系,以一个button组件为例:

Button是一个小window,具体地,它是继承自wx.Control这一类的window(有些组件是window,但不是继承自wx.Control,比如wx.Dialog,更具体来说,controls这类组件是可放置在containers这类组件上的组件)。同时所有的windows都可以响应事件,button也不例外,因此它还继承自wx.EvtHandler。最后,所有的wxpython对象都继承自wx.Object类。

wxPython的“你好世界”

这个例子是wxPython的最小可用例子,用来say hello to the world:

import wxapp = wx.App()frame = wx.Frame(None, title='Hello World')frame.Show()app.MainLoop()

麻雀虽小五脏俱全,该例子包含了最基本的代码和组件:

(1)首先导入wxPython库:

import wx

wx可视为一个命名空间,后面所有的函数和类都以它开头。

(2)创建应用实例:

app = wx.App()

每一个wxPython程序都必须有一个应用实例。

(3)创建应用框架并显示:

frame = wx.Frame(None, title='Hello World')frame.Show()

这里创建了一个wx.Frame对象。wx.Frame是一个重要的“容器”组件,它用来承载其他组件,它本身没有父组件(如果我们给组件的parent参数设为None,即代表该组件没有父组件)。创建该对象后,还需调用Show方法才能显示出来。

wx.Frame的构造函数一共有七个参数:

wx.Frame(wx.Window parent, int id=-1, string title='', wx.Point pos=wx.DefaultPosition, wx.Size size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, string name="frame")

除了第一个parent参数需要显式指定,其余六个都有默认值,包括ID、名称、位置、尺寸和样式等。因此,可以通过改变这些参数来进一步地对该frame进行个性化定制。

(4)启动程序主循环:

app.MainLoop()

程序的主循环是一个无限循环模式,它捕获并分发程序生命周期内的所有事件。

菜单栏和工具栏

菜单栏

菜单栏主要由三部分组成:wx.MenuBar、wx.Menu和wx.MenuItem。

在菜单栏MenuBar中可以添加菜单Menu,在菜单Menu中又可以添加菜单项MenuItem。

添加完后不要忘了使用SetMenuBar来将菜单栏加入到框架中。

进一步地,在某个菜单Menu中,还可以添加子菜单SubMenu,然后继续添加菜单项。

还可以给菜单设置图标、快捷键、对wx.EVT_MENU事件的动作、菜单样式(打勾、单选)等。

上下文菜单

上下文菜单有时叫做“弹出菜单”,比如右键某个位置,出现上下文选项。

工具栏

工具栏的添加也是类似流程:先添加工具栏CreateToolBar,然后在上面添加工具AddTool。

别忘了使用toolbar.Realize()使之呈现出来(这一步与操作系统有关,Linux上不强制使用,Windows必须使用,为了跨平台性,最好将这一步明确写出)。

对于某个工具,可以设置逻辑使之Enable或Disable,常见的比如undo和redo,这两个按钮不是一直可以点的,在最开始时redo就必须是disabled,因为没有历史操作,所以可以设置具体的逻辑使之disable掉。

状态栏

状态栏即底部显示当前状态的状态条。

布局管理

布局可以分为绝对布局和布局管理器sizer。绝对布局有很多缺点,比如:

(1)组件的尺寸和位置不随窗口的改变而改变;

(2)不同平台上应用程序可能显示不同;

(3)字体的改变可能破坏布局;

(4)如果想改变布局,必须将之前的全部推翻。

因此,推荐使用布局管理器sizer来管理布局。

wxPython常用的sizer有:wx.BoxSizer、wx.StaticBoxSizer、wx.GridSizer、wx.FlexGridSizer、wx.GridBagSizer。

wx.BoxSizer

wx.BoxSizer是最常见的布局管理器。它的常用设置有:

(1)排列方向:wx.VERTICAL垂直排列还是wx.HORIZONTAL水平排列;

(2)排列比例:一个布局中所包含的组件的尺寸由其比例所决定,比例为0表示在窗口尺寸变化时保持尺寸不变,其他比例系数表示组件在该布局管理器中的尺寸占比;且通常使用wx.EXPAND旗标来使得组件占据管理器分配给它的所有空间;

(3)边界:组件的边界大小可以自定义设置,同时具体哪个边界(上下左右或全部)都可以任意指定;

(4)对齐方式:可以设定左端对齐、右端对齐、顶部对齐、底部对齐、中心对齐等多种对齐方式;

(5)在某一级容器组件中,使用SetSizer()来为其指定布局管理器;

(6)在布局管理器中用Add()方法来添加组件。

wx.StaticBoxSizer是在BoxSizer周围加上了一个静态文本框的显示。

wx.GridSizer

wx.GridSizer是网格布局管理器,可以设置几排几列以及横纵的间距,网格中的组件尺寸都是相同的。

(如果有的网格不需要添加组件,可以添加没有内容的StaticText作为占位符)

wx.FlexGridSizer

wx.FlexGridSizer与wx.GridSizer类似,但其更灵活,它不要求网格中所有的组件尺寸都相同,而是在同一行中的所有组件都高度相同,而同一列中的所有组件都宽度相同。

它还可以设置能growable的行和列,即在当前sizer中如果有空间,就将特定的行和列调整相应的大小来占据这个空间(注意将该行或列中的组件设为expandable)。

wx.GridBagSizer

wx.GridBagSizer是wxPython中最灵活的sizer(不仅仅是wxPython,其他函数库也有类似的配置),它可以显式地指定sizer中组件所占据的区域,比如横跨几行几列等。

它的构造函数很简单:

wx.GridBagSizer(integer vgap, integer hgap)

只需设定间距,然后通过Add()方法添加组件:

Add(self, item, tuple pos, tuple span=wx.DefaultSpan, integer flag=0, integer border=0, userData=None)

pos参数指定组件在这个虚拟网格中的起始位置,(0, 0)就代表左上角,span就指定它横跨几行几列,比如(3, 2)代表占据3行2列。

如果想组件可以随窗口伸缩,别忘了设置expandle属性,及:

AddGrowableRow(integer row)AddGrowableCol(integer col)

Sizer常见问题

大部分的问题出现在:

(1)设置比例proportional错误,只有需要随窗口变化的组件和sizer才需要设置为非0,其他都设置为0。且sizer和里面的组件可分别设置,比如下面的:

self.panel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )vbox = wx.BoxSizer( wx.VERTICAL )hbox1 = wx.BoxSizer( wx.HORIZONTAL )self.st1 = wx.StaticText( self.panel, wx.ID_ANY, u"Class Name", wx.DefaultPosition, wx.DefaultSize, 0 )self.st1.Wrap( -1 )hbox1.Add( self.st1, 0, wx.RIGHT, 8 )self.tc = wx.TextCtrl( self.panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )hbox1.Add( self.tc, 1, 0, 5 )vbox.Add( hbox1, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 10 )

在vbox中添加了hbox1,hbox1中又添加了静态文本框st1和输入框tc,hbox1的比例为0,代表在vbox这一垂直排列的管理器变化时,hbox1尺寸不变化,但tc的比例又为1,所以vbox在垂直变化时,tc按着hbox1不变化,但vbox水平变化时,tc就会随着变化。这样就有非常高的适应性。

(总结起来:看是否expandable要看组件所在的sizer!!)

(2)边界border尺寸设置不统一,导致对不齐

(3)Expandable属性和proportion两个中有一个忘了设置,导致组件不能随窗口伸缩。

这个sizer的编写此处可以借助wxFormBuilder工具来进行设计,实现所想即所得。(wxFormBuilder能够实现即时的改变,但此处遇到一个小问题,在wxGridBagSizer设置了某列进行可伸缩后,在wxFormBuilder中却不能正确伸缩,反而generate code后直接调用能正确伸缩,所以也不能完全相信,但可以99%相信,实在调不通后可以换种运行方式接着调)

事件

事件

事件是一个图形界面程序的核心部分,任何图形界面程序都是事件驱动的。

事件可以有多重产生方式,大部分是用户触发的,也有可能由其他方式产生,比如网络连接、窗口管理和计时器调用等。

关于事件,这里面有几个过程和要素:

事件循环Event Loop(比如wxPython的MainLoop()方法),它一直在寻找和捕获事件Event(比如wx.EVT_SIZE、wx.EVT_CLOSE等);当捕获到事件后,就通过分发器dispatcher将事件分发到事件句柄Event Handler(事件句柄是对事件进行响应的动作方法);事件本身与事件句柄的映射由Event Binder来完成(即Bind()方法)。

对用户编程来说,最常打交道的就是Bind()方法:

Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)

溯源起来,该Bind()方法是定义在EvtHandler类中,而EvtHandler又派生了Window类,Window类又是绝大多数组件的父类,因此可以在组件中直接使用该方法(如果想将事件解绑,则可以调用Unbind()方法,其参数跟下面的参数相同)。

event参数就是事件对象,它指定了事件类型;

handler就是对该事件的响应方法,这个通常要由编程自定义完成;

source是指该事件来自于哪个组件,比如很多组件都能产生同样的事件,就需要指定具体的来源,比如很多button都能产生鼠标点击事件。这里面就有一个很tricky的地方,假设self是一个panel,该panel上有很多buttons,名为bt1、bt2,那么self.Bind(event, handler, source=self.bt1)和self.bt1.Bind(event, handler)有什么区别呢?两者看起来的效果是相同的, 这里有一个帖子详细说明了两者的区别 ;

id是通过ID来指定事件来源,而上面的source是通过直接指定实例,两者目的相同;关于组件的ID,主要有两种创建方式:

(1)让系统自动创建:即使用-1或wx.ID_ANY,系统自动创建的ID都是负数,因此用户自己创建的ID都应该是正数,此种情况通常用于不用改变状态的组件。可以使用GetId()来获取该隐形id;

(2)标准ID:wxPython提供了一些标准IDs,比如wx.ID_SAVE、wx.ID_NEW等;

id2是指定多个IDs,上面的id是一次只能指定单个ID。

这里面有个很好玩的用法,如果想批量给多个同类组件绑定事件,可以用lambda函数,比如:

#!/usr/bin/python# -*- coding: UTF-8 -*-__author__ = 'huangbinghe@gmail.com'import wxclass TestFrm(wx.Frame): """TestFrm""" def __init__(self, *arg, **kw): super().__init__(*arg, **kw) panel = wx.Panel(self, -1) box = wx.BoxSizer(wx.VERTICAL) for i in range(5): btn = wx.Button(panel, -1, label="test-{}".format(i)) btn.Bind(wx.EVT_BUTTON, lambda e, mark=i: self.on_click(e, mark)) box.Add(btn, 0, wx.LEFT) panel.SetSizer(box) def on_click(self, event, mark): wx.MessageDialog(self, 'click mark:{}'.format( mark), 'click btn', wx.ICON_INFORMATION).ShowModal() if __name__ == '__main__': app = wx.App() frm = TestFrm(None, title="hello world") frm.Show() app.MainLoop()

事件传播

有两种类型的事件:basic events和command events。它们两者的区别在于是否传播上。事件的传播是指事件从触发该事件的子组件开始,传递给其父组件,并观察其响应。Basic events不传播,而command events传播。比如wx.CloseEvent就是一个basic event,它不传播,因为如果传播给父组件就很没有道理。

默认情形下,在事件句柄中的事件是阻止传播的,如果想让它继续传播,需要调用skip()方法(这个也解释了上面的self.Bind(event, handler, source=self.bt1)和self.bt1.Bind(event, handler)的区别)。

常见事件

窗口移动事件:wx.EVT_MOVE

窗口销毁事件:wx.EVT_CLOSE,发生在点击工具栏的关闭按钮、Alt F4或从开始菜单关闭计算机时(注意销毁窗口是destroy()方法)

按钮事件:wx.EVT_BUTTON,点击一个按钮时

菜单事件:wx.EVT_MENU,点击一个菜单时

绘图事件:wx.EVT_PAINT,改变窗口尺寸或最大化窗口时(最小化窗口时不会产生该事件)

焦点事件:wx.EVT_SET_FOCUS,当某组件成为焦点时;wx.EVT_KILL_FOCUS,当某组件失去焦点时

键盘事件:wx.EVT_KEY_DOWN,键盘按下;wx.EVT_KEY_UP,键盘弹起;wx.EVT_CHAR,这个应该是为了兼容非英语字符。

对话框

对话框是一种非常重要的人机交互的手段,可以使得用户输入数据、修改数据、更改程序配置等。

预定义的消息对话框

消息对话框是为了向用户展示消息,可以通过一些预定义的旗标来定制消息对话框的按钮和图标,如下图所示:

自定义对话框

若想自定义对话框,只需继承wx.Dialog即可。

常用组件

基础组件

wxPython提供了大量基础组件,如:

基础按钮Button;

图形按钮BitmapButton;

切换按钮ToggleButton(有两种状态可以切换:按下和未按下);

静态文本框StaticText(展示一行或多行只读文本);

文本输入框TextCtrl;

富文本输入框RichTextCtrl可以加入图像、文字色彩等效果;

带格式文本输入框StyledTextCtrl;

超链接HyperLinkCtrl;

静态位图:StaticBitmap;

静态分割线StaticLine(可垂直可水平);

静态框StaticBox(为了装饰用,将多个组件组合在一起显示);

下拉列表框ComboBox;

可编辑的下拉列表框Choice;

复选框CheckBox(有两个状态:勾选或未勾选);

单选按钮RadioButton(单选按钮是从一组选项中只能选择一个,将多个单选按钮组合成一个选项组时,只需设定第一个单选按钮style为wx.RB_GROUP,后面跟着的那些单选按钮就自动跟它一组,如果想另开一组,只需再将另一组的第一个单选按钮的style设置为wx.RB_GROUP);

进度条Gauge;

滑动条Slider;

整数数值调节钮SpinCtrl;

浮点数数值调节钮SpinCtrlDouble;

滚动条ScrollBar。

高级组件

列表框ListBox:是对一组选项的展示和交互,它有两个主要的事件,一个是wx.EVT_COMMAND_LISTBOX_SELECTED,即鼠标单击某一项时产生;另一个是wx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED,即鼠标双击某一项时产生。

列表视图ListCtrl:也是用来展示一组选项,与ListBox不同的是,ListBox仅能展示一列,而ListCtrl能展示多列。ListCtrl有三种视图模式:list、report和icon。向ListCtrl中插入数据需要使用两种方法:首先使用InsertItem()方法获得行号,然后再在当前行中使用SetItem()方法在列中插入数据。

Mixins:Mixins增强了ListCtrl的功能,它们都在wx.lib.mixins.listctrl这个模块中,一共有六种Mixins:

(1)wx.ColumnSorterMixin:使得在report视图中对列进行排序;

(2)wx.ListCtrlAutoWidthMixin:自动调整最后一列的宽度来占据剩余的空间;

(3)wx.ListCtrlSelectionManagerMix:定义了与系统无关的选择策略;

(4)wx.TextEditMixin:使得可以编辑文本;

(5)wx.CheckListCtrlMixin:给每一行增加了一个复选框;

(6)wx.ListRowHighlighter:候选行自动背景高亮。

wx.html.HtmlWindow:用来展示HTML页面。

wx.SplitterWindow:包含两个子窗口(如果使用wxFormBuilder,注意手动添加上两个panel)

另外还有比如:

树状结构TreeCtrl;

表格Grid;

搜索框SearchCtrl;

调色板ColourPickerCtrl;

字体设置器FontPickerCtrl;

文件选择器FilePickerCtrl;

文件目录选择器DirPickerCtrl;

文件树选择器GenericDirCtrl;

日期选择器DatePickerCtrl;

日历CalenderCtrl。

绘图

wxPython的绘图之前写过,参见以下两篇:

ImagePy解析:6 — wxPython GDI绘图和FloatCanvas

ImagePy解析:11 — 使用wxPython设备上下文绘图

自定义组件

如上,wxPython的常用组件已经有很多,但仍然不能涵盖真实情况下的千奇百怪的需求,这时候就要根据自己的需求自定义组件。

自定义组件有两种方式:一种是在现有组件的基础上修改或增强,这种方式仍然有一定的限制;另一种是结合wxPython的GDI绘图,自己从头创建组件,这种方式就具有极大的灵活性。

从头绘制组件一般都是在wx.Panel基础上进行创建。

俄罗斯方块

下面给了一个俄罗斯方块的游戏程序代码,可以说是一个使用wxPython编写GUI程序的集大成者:

#!/usr/bin/python"""ZetCode wxPython tutorialThis is Tetris game clone in wxPython.author: Jan Bodnarwebsite: www.zetcode.comlast modified: April 2018""" import wximport randomclass Tetris(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, size=(180, 380), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX) self.initFrame() def initFrame(self): self.statusbar = self.CreateStatusBar() self.statusbar.SetStatusText('0') self.Board = Board(self) self.board.SetFocus() self.board.start() self.SetTitle("Tetris") self.Centre()class Board(wx.Panel): BoardWidth = 10 BoardHeight = 22 Speed = 300 ID_TIMER = 1 def __init__(self, *args, **kw): # wx.Panel.__init__(self, parent) super(Board, self).__init__(*args, **kw) self.initBoard() def initBoard(self): self.timer = wx.Timer(self, Board.ID_TIMER) self.isWaitingAfterLine = False self.curPiece = Shape() self.nextPiece = Shape() self.curX = 0 self.curY = 0 self.numLinesRemoved = 0 self.board = [] self.isStarted = False self.isPaused = False self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER) self.clearBoard() def shapeAt(self, x, y): return self.board[(y * Board.BoardWidth) x] def setShapeAt(self, x, y, shape): self.board[(y * Board.BoardWidth) x] = shape def squareWidth(self): return self.GetClientSize().GetWidth() // Board.BoardWidth def squareHeight(self): return self.GetClientSize().GetHeight() // Board.BoardHeight def start(self): if self.isPaused: return self.isStarted = True self.isWaitingAfterLine = False self.numLinesRemoved = 0 self.clearBoard() self.newPiece() self.timer.Start(Board.Speed) def pause(self): if not self.isStarted: return self.isPaused = not self.isPaused statusbar = self.GetParent().statusbar if self.isPaused: self.timer.Stop() statusbar.SetStatusText('paused') else: self.timer.Start(Board.Speed) statusbar.SetStatusText(str(self.numLinesRemoved)) self.Refresh() def clearBoard(self): for i in range(Board.BoardHeight * Board.BoardWidth): self.board.append(Tetrominoes.NoShape) def OnPaint(self, event): dc = wx.PaintDC(self) size = self.GetClientSize() boardTop = size.GetHeight() - Board.BoardHeight * self.squareHeight() for i in range(Board.BoardHeight): for j in range(Board.BoardWidth): shape = self.shapeAt(j, Board.BoardHeight - i - 1) if shape != Tetrominoes.NoShape: self.drawSquare(dc, 0 j * self.squareWidth(), boardTop i * self.squareHeight(), shape) if self.curPiece.shape() != Tetrominoes.NoShape: for i in range(4): x = self.curX self.curPiece.x(i) y = self.curY - self.curPiece.y(i) self.drawSquare(dc, 0 x * self.squareWidth(), boardTop (Board.BoardHeight - y - 1) * self.squareHeight(), self.curPiece.shape()) def OnKeyDown(self, event): if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape: event.Skip() return keycode = event.GetKeyCode() if keycode == ord('P') or keycode == ord('p'): self.pause() return if self.isPaused: return elif keycode == wx.WXK_LEFT: self.tryMove(self.curPiece, self.curX - 1, self.curY) elif keycode == wx.WXK_RIGHT: self.tryMove(self.curPiece, self.curX 1, self.curY) elif keycode == wx.WXK_DOWN: self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY) elif keycode == wx.WXK_UP: self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY) elif keycode == wx.WXK_SPACE: self.dropDown() elif keycode == ord('D') or keycode == ord('d'): self.oneLineDown() else: event.Skip() def OnTimer(self, event): if event.GetId() == Board.ID_TIMER: if self.isWaitingAfterLine: self.isWaitingAfterLine = False self.newPiece() else: self.oneLineDown() else: event.Skip() def dropDown(self): newY = self.curY while newY > 0: if not self.tryMove(self.curPiece, self.curX, newY - 1): break newY -= 1 self.pieceDropped() def oneLineDown(self): if not self.tryMove(self.curPiece, self.curX, self.curY - 1): self.pieceDropped() def pieceDropped(self): for i in range(4): x = self.curX self.curPiece.x(i) y = self.curY - self.curPiece.y(i) self.setShapeAt(x, y, self.curPiece.shape()) self.removeFullLines() if not self.isWaitingAfterLine: self.newPiece() def removeFullLines(self): numFullLines = 0 statusbar = self.GetParent().statusbar rowsToRemove = [] for i in range(Board.BoardHeight): n = 0 for j in range(Board.BoardWidth): if not self.shapeAt(j, i) == Tetrominoes.NoShape: n = n 1 if n == 10: rowsToRemove.append(i) rowsToRemove.reverse() for m in rowsToRemove: for k in range(m, Board.BoardHeight): for l in range(Board.BoardWidth): self.setShapeAt(l, k, self.shapeAt(l, k 1)) numFullLines = numFullLines len(rowsToRemove) if numFullLines > 0: self.numLinesRemoved = self.numLinesRemoved numFullLines statusbar.SetStatusText(str(self.numLinesRemoved)) self.isWaitingAfterLine = True self.curPiece.setShape(Tetrominoes.NoShape) self.Refresh() def newPiece(self): self.curPiece = self.nextPiece statusbar = self.GetParent().statusbar self.nextPiece.setRandomShape() self.curX = Board.BoardWidth // 2 1 self.curY = Board.BoardHeight - 1 self.curPiece.minY() if not self.tryMove(self.curPiece, self.curX, self.curY): self.curPiece.setShape(Tetrominoes.NoShape) self.timer.Stop() self.isStarted = False statusbar.SetStatusText('Game over') def tryMove(self, newPiece, newX, newY): for i in range(4): x = newX newPiece.x(i) y = newY - newPiece.y(i) if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight: return False if self.shapeAt(x, y) != Tetrominoes.NoShape: return False self.curPiece = newPiece self.curX = newX self.curY = newY self.Refresh() return True def drawSquare(self, dc, x, y, shape): colors = ['#000000', '#CC6666', '#66CC66', '#6666CC', '#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00'] light = ['#000000', '#F89FAB', '#79FC79', '#7979FC', '#FCFC79', '#FC79FC', '#79FCFC', '#FCC600'] dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80', '#80803B', '#803B80', '#3B8080', '#806200'] pen = wx.Pen(light[shape]) pen.SetCap(wx.CAP_PROJECTING) dc.SetPen(pen) dc.DrawLine(x, y self.squareHeight() - 1, x, y) dc.DrawLine(x, y, x self.squareWidth() - 1, y) darkpen = wx.Pen(dark[shape]) darkpen.SetCap(wx.CAP_PROJECTING) dc.SetPen(darkpen) dc.DrawLine(x 1, y self.squareHeight() - 1, x self.squareWidth() - 1, y self.squareHeight() - 1) dc.DrawLine(x self.squareWidth() - 1, y self.squareHeight() - 1, x self.squareWidth() - 1, y 1) dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(wx.Brush(colors[shape])) dc.DrawRectangle(x 1, y 1, self.squareWidth() - 2, self.squareHeight() - 2)class Tetrominoes(object): NoShape = 0 ZShape = 1 SShape = 2 LineShape = 3 TShape = 4 SquareShape = 5 LShape = 6 MirroredLShape = 7class Shape(object): coordsTable = ( ((0, 0), (0, 0), (0, 0), (0, 0)), ((0, -1), (0, 0), (-1, 0), (-1, 1)), ((0, -1), (0, 0), (1, 0), (1, 1)), ((0, -1), (0, 0), (0, 1), (0, 2)), ((-1, 0), (0, 0), (1, 0), (0, 1)), ((0, 0), (1, 0), (0, 1), (1, 1)), ((-1, -1), (0, -1), (0, 0), (0, 1)), ((1, -1), (0, -1), (0, 0), (0, 1)) ) def __init__(self): self.coords = [[0,0] for i in range(4)] self.pieceShape = Tetrominoes.NoShape self.setShape(Tetrominoes.NoShape) def shape(self): return self.pieceShape def setShape(self, shape): table = Shape.coordsTable[shape] for i in range(4): for j in range(2): self.coords[i][j] = table[i][j] self.pieceShape = shape def setRandomShape(self): self.setShape(random.randint(1, 7)) def x(self, index): return self.coords[index][0] def y(self, index): return self.coords[index][1] def setX(self, index, x): self.coords[index][0] = x def setY(self, index, y): self.coords[index][1] = y def minX(self): m = self.coords[0][0] for i in range(4): m = min(m, self.coords[i][0]) return m def maxX(self): m = self.coords[0][0] for i in range(4): m = max(m, self.coords[i][0]) return m def minY(self): m = self.coords[0][1] for i in range(4): m = min(m, self.coords[i][1]) return m def maxY(self): m = self.coords[0][1] for i in range(4): m = max(m, self.coords[i][1]) return m def rotatedLeft(self): if self.pieceShape == Tetrominoes.SquareShape: return self result = Shape() result.pieceShape = self.pieceShape for i in range(4): result.setX(i, self.y(i)) result.setY(i, -self.x(i)) return result def rotatedRight(self): if self.pieceShape == Tetrominoes.SquareShape: return self result = Shape() result.pieceShape = self.pieceShape for i in range(4): result.setX(i, -self.y(i)) result.setY(i, self.x(i)) return resultdef main(): app = wx.App() ex = Tetris(None) ex.Show() app.MainLoop()if __name__ == '__main__': main()

效果如图:

不过我在运行上述代码时,出现了无法使用箭头键来控制方块的情形,解决方式在Board这个panel中设置一个旗标:

super(Board, self).__init__(*args, **kw, style=wx.WANTS_CHARS)

该问题的讨论在:

how to catch arrow keys ?

Stumped: arrows/tab kills keyboard focus

另外,捕获keycode,如果是判断字母,最好是大小写形式都判断,即里面:

if keycode == ord('P') or keycode == ord('p'):

每天只更新一题的小游戏,是如何让大家上瘾的

本文来自微信公众号:DT财经(ID:DTcaijing),作者:高雅馨,编辑:金花鼠、唐也钦,题图来自:Wordle

日均摸鱼4小时,把办公室当成自家卧室的他,最近常常眉头紧锁、目光凝重,盯着屏幕仿佛在思考重大的哲学命题。但当你好奇地凑近,会发现原来他只是在玩汉兜——一款网页版成语猜词小游戏。

而另一款名叫Wordle的英文版填词游戏,更早就在互联网上火了起来。在2021年底,海外各大社交平台开始日渐被绿色、黄色、灰色和黑色色块组成的神秘图案刷屏。满屏色块,看似某种加密讯息,又像是毫无秩序的俄罗斯方块。而它们的真实身份,其实是玩家们的Wordle战绩。

(网友们正在国内外互联网平台上“带Wordle冲浪” 图源:小红书&Twitter&微博)

一众号称用PS5、Xbox镇宅,迷恋3A大作又涉猎各类手游的年轻人,对这个满屏色块的猜词游戏上头了。

随着越来越多玩家涌入Wordle的世界,我们好奇,这个益智小游戏是怎么火的?它又在哪些方面满足了现代人的需求?

1. 最近火起来的Wordle,是什么?

首先,让我们为第一次听说Wordle的新玩家们进行一个简单的科普。已经熟悉这部分内容的朋友可以选择跳过。

Wordle是一款英文猜词小游戏,每天会更新一个由五个字母组成的英文单词。而玩家的目的,就是根据提示在6次机会之内猜出正确答案。

(游戏初始界面,图源:Wordle)

玩法非常简单,玩家只需要在文本框里随机输入一个由五个字母组成的单词,如water、other、actor等,即可开始一轮游戏。至于关于谜底单词的线索,则要根据字母颜色进行判断。

如果字母表现为绿色,就说明答案中有这个字母,且位置正确;以黄色出现,说明字母猜对了,但位置错误;以灰色出现,说明答案中完全没有这个字母。

(2022年2月15日的正确答案,图源:修高)

以2月15日的Wordle题目为例,我们在首轮尝试中打出了“VALID”一词,其中字母A呈黄色,说明谜底单词中包含A这个字母,只是位置不正确。

第二次尝试中,我们换了一个既包含字母A,位置又与第一次不同的单词“ABACK”。结果处于首位的字母A呈绿色,表示答案单词正是以A开头。

于是在第三次尝试中,我们输入了由A打头的“AGREE”,并得到了关于黄色字母R的新线索。

Wordle的特点是在输入一个单词之后即可根据排除法,对下一个新单词进行排列组合。就这样,我们用简单粗暴的“想到哪填到哪”的猜词方式,经过6次尝试也成功筛选出了正确答案。值得注意的是,该游戏每天只能玩一次,每天0点准时更新题目。

相比其他画风精美、玩法多样的高阶手游,Wordle就好像报纸上的益智小游戏,考验玩家的词汇量、英语语感和运气。而这个让人绞尽脑汁回忆高中英语课的游戏背后,其实还有个浪漫的爱情故事。

Wordle的开发者Josh Wardle是布鲁克林的一名程序员。为了帮妻子在疫情期间解闷,他发明了这个简易填字游戏,并以自己的姓氏将游戏命名为Wordle。

最初,Wordle的受众只有开发者的家人,直到 2021 年 10 月,Josh Wardle将游戏发布在他的个人网站上,Wordle从此进入公共视野,并获得了极好的反响。

据《纽约时报》报道,2021年11月1日,Wordle的玩家只有90人。短短两个月后,就有超过30万人在线填词;截至2022年2月中旬,Wordle的日均活跃用户更是超过百万,并仍在高速增长。

2. Wordle是怎么火的?

Wordle的爆红,应该归功于它的社交分享功能。

如今的社交媒体不仅扮演着交流工具的角色,也是人们的线上生活区域。在高度流动的现代社会中,社交媒体可以弥合迁移所带来的社会断裂。人们在社交平台分享自己的生活,并与他人产生共鸣,对于分享者来说就可以收获“合群”所带来的安全感。

或是出于揭开谜底的喜悦,或是为了吐槽题目太难,在完成Wordle后即刻出现的分享按键,使玩家把自己当下的情绪带到更多人面前。Wordle也在人们的不断分享的过程中,成为社交货币一般的存在。

(“这么难答,还有王法吗?还有法律吗” 图源:Twitter)

(第二次就猜中正确答案=“I am better than everyone” 图源 Twitter)

Wordle是一个猜谜游戏,分享答案即为泄露谜底,这原本是一件让其他玩家深感“下头”的事。但在2021年12月,玩家 @irihapeta突发奇想,创造出了能够让游戏参与者既不泄题、也能分享他们的游戏结果的办法——正是我们在一开始就提到的绿色、黄色、灰色和黑色的Emoji表情。

Emoji的排列逻辑与游戏的提示方式相对应,分享者可以用不同颜色的色块,将自己的闯关过程用“手动打码”的方式呈现在网友面前。

(游戏开发者Josh Wardle向@irihapeta的创意隔空致敬 图源:Twitter)

很快,这些由绿绿黄黄拼接而成的神秘图案,就犹如病毒般蔓延至互联网的各个角落。每条图文结合的发布内容,再挂上令人们更有归属感的各种关于Wordle话题标签,俨然一副会员制的形象。神秘感多层叠加,吸引了源源不断的新鲜玩家加入游戏。

随着游戏的火热度不断提升,2022年1月31日,《纽约时报》以“七位数的未公开价格”收购了Wordle,并对其进行了小幅度改版。目前需要通过翻墙的方式,才能开启Wordle。

而Wordle的爆红,也给其他语言的开发者提供了可供模仿的思路。比如中文的“汉兜”、 日语的“漢字ル”等。

据汉兜的开发者形容:汉兜很大程度上继承了 Wordle 的玩法,与之不同的是,汉兜的开发者将每个汉字拆分成 汉字、声母、韵母、声调 四个部分,各自进行颜色匹配。

每次游戏开始,玩家通常可以通过两次提示,获得关于该成语1/4的核心信息,接下来就要靠玩家的词汇量,或是百度搜索和交叉分析能力。

(2022年2月15日的handle词汇 图源:馨仔)

玩家将有十次机会中猜一个四字成语,这考验的是玩家对于成语的掌握度。

3. 大家为什么喜欢玩Wordle(填字游戏)?

回顾填字游戏的起始,1913年12月21日,亚瑟·威恩设计出了世界上第一个填字游戏,并在《纽约世界报》发表。那时,报纸还是人们生活中不可或缺的信息来源。以报纸为载体的填字游戏,在1920年代的美国一度风靡。

时间来到21世纪,在游戏类型越来越丰富的今天,古老的填字游戏为什么生命力依然旺盛?曾经属于填字游戏的“高光时刻”又是为何得以复现?我们分析了几方面的因素。

获得成就感

不满足于一成不变的生活状态,突破困难、解决麻烦是人们获得即刻成就感的来源,也是很多填字游戏玩家热衷于解谜的原因。

这也顺应了填词游戏的行为逻辑。

《纽约时报》填字游戏编辑威尔·肖茨曾针对填字游戏提出了“自然讨厌真空”的理论。他认为如果有人看到了一个空的黑白网格,就会忍不住想要填充它。

而一旦破解了谜题,填满了空白,即时的成就感就油然而生。“像是做数学试卷的最后一大题,好不容易做出来了,就忍不住告诉所有人”,编辑部的小美这样评价每次做出Wordle的感觉。

学到新知识

在填字游戏的设定中,玩家通常被看作是具有一定文化水平和解决问题的能力的人。

以Wordle为例,它每次给出的词汇间并没有什么特殊联系,但玩家对于词汇的掌握程度则能直接决定答题的步骤、速度和正确率。在无法猜透答案时,也有不少玩家选择在网上寻找答案,这一过程即为学习。

社交货币

自从有了Wordle,一起解题、互相提示答案就成了编辑部日常生活的一部分。“做没做今天的Wordle?”成为可以取代“吃了吗?“的新型社交问候语。

在线上,我们也在不同的社交平台中发现了Wordle的身影。通过分享自己对于游戏的看法,网友和网友之间的关系升华到了“既可意会、也可言传”的境界,这在如今“裙子到底是蓝黑色还是白金色都能吵n年”的互联网大环境中也实属不易。

最终,话题回归Wordle。虽然游戏的规则很简单,但想达成游戏成就也少不了“金手指”。于是我们帮大家搜罗了一些关于更容易实现“弯道超车”的tips:

最容易出现的字母排行:


1、E(12.49% of words)


2、T(9.28% of words)


3、A(8.04% of words)


4、O(7.64% of words)


5、I(7.57% of words)


6、N(7.23% of words)


7、S(6.51 of words)


8、R(6.28% of words)


9、H(5.05 of words)


10、L(4.07% of words)


11、D(3.82% of words)


12、C(3.34% of words)

注:未经过证实,只做娱乐性质参考。

写在最后

从前,人们对于游戏的要求有很多:全新的玩法、有面儿的装备、高级的交互方式......

但正是如此简易的一款填字游戏,却能够让人们静下心来,花5分钟、10分钟培养自己的专注力,在这一刻抛却浮躁向内发现自己。

从提高生活追求的角度出发,如今人们或许更期待在游戏中获得纯粹的快乐。去繁从简,成为不少玩家评价一个好游戏的关键词,甚至没有之一。

参考文献

1. DoNews, 热门猜词游戏 Wordle 网站迁移到《纽约时报》,2022.

2. 硅星人,一款不肯赚钱的游戏爆红,它的抄袭者正被全网“追杀”,2022.

3. "Wordle Is Joining The New York Times Games". The New York Times Company. 31 January 2022. Retrieved 5 February 2022.

4. 全媒派朱亦祺,100年过去了,为什么国外一些大报还热衷于搞填字游戏?,2021.

5. Augarde, Tony (2003). The Oxford Guide to Word Games. Oxford: Oxford University Press. ISBN 0-19-866264-5.

本文来自微信公众号:DT财经(ID:DTcaijing),作者:高雅馨,编辑:金花鼠、唐也钦

本内容为作者独立观点,不代表虎嗅立场。未经允许不得转载,授权事宜请联系 hezuo@huxiu.com

正在改变与想要改变世界的人,都在 虎嗅APP

游戏史上的今天:俄罗斯方块GameBoy版

为GameBoy奠定市场地位的重要作品,让俄罗斯方块的魅力拓展到更广阔的人群中。

1989年6月14日,基于原版《俄罗斯方块》的GameBoy移植版发售,与体验相对比较诡异的FC版相比,这个掌机版本明显流传更广。黑白两色的画面本身或许有些单调,但却完整保留了《俄罗斯方块》神奇的游戏乐趣。除此之外,这是第一款支持经由GameBoy通信线缆进行对战的作品,这对后来很多游戏起到了启发性的作用。

GB版的《俄罗斯方块》玩起来无论是基本理念还是关卡结构都和其他版本没有太大区别,还是那个基于伪随机原则的四格骨牌拼图游戏,事实证明任天堂将这款游戏与几个月前发售的GameBoy进行捆绑销售是一个非常明智的举措,它充分展示了GameBoy的便携性、续航性能以及在特定领域的强大表现力,这对于便携游戏概念的推广十分重要。

本作的双人模式主要比谁存活的时间更长,玩家在通过数据线相连的两台GB上进行自己的游戏,一旦有一方同时消掉2排或2排以上的方块,对方游戏画面的最下方就会增加一排垫高方块。这显然是后世很多“方块对战”类游戏的雏形,只不过受机能的限制没有各种互害道具等设定。

其实在“俄罗斯方块”缔造者阿列克谢·帕基特诺夫最初的设想中,这就应该是一款支持双人对战的游戏,但估计这位“俄罗斯方块”之父怎么也想不到日后围绕这一IP的版权会发生那么多的纠葛,GameBoy版诞生前后围绕任天堂、Spectrum、Mirrorsoft、Bullet-Proof Software等数家公司的各种你来我往就足够写成一本书,甚至惊动了前苏联政府当局。

那台著名的“毁容GB”,海湾战争期间很多士兵用俄罗斯方块来打发时间

游戏最终的授权费用至今仍是个迷,关于这一点业界流传着很多的说法,要知道荒川实(山内溥女婿)以及任天堂北美当时的负责人霍华德·林肯当时全都亲赴莫斯科助阵,可以想象是怎样的一笔天价。

GB版《俄罗斯方块》全球销量超过3000万份,这还没有算上数量巨大的各种盗版产品,在该平台仅次于另一怪物级软件《口袋妖怪红·绿》。任天堂曾在1998年以GameBoy Color为平台对这款游戏进行了重制,取名《俄罗斯方块DX》,支持电池储存以及有三个存档位,还有全新的对CPU模式、Ultra模式以及40行模式,但总的来讲游戏的核心乐趣没有太大变化。

重制版的《俄罗斯方块DX》

随着2014年12月31日GB版《俄罗斯方块》疑似因为版权纠纷从3DS的eShop下架,这款经典游戏终于结束了它在任天堂阵营的旅程,但留下的却是数不清的美好回忆。

  • 最新游戏
  • 发表评论
手游排行 新游中心 热门专区 手机软件APP下载
网游排行榜 游戏攻略 网游下载 安卓软件APP下载
单机排行榜 手游礼包 单机下载 苹果ios应用下载
安卓排行榜 新游视频 手游下载
苹果排行榜