[Small idea / question] Customizing Glimmer Tetris?
rubyFeedback opened this issue · 1 comments
Heya andy,
This is mostly a small-ish question.
Is it possible to "customize" tetris? Specifically colours and
perhaps gradient for use of the individual blocks.
I assume some customization is possible e. g. you use:
block_size: BLOCK_SIZE
So I suppose one can tweak that. But is the same possible
for colours/fill? Can there be a black border around individual
tetris elements? like a 1px solid black border?
What about gradients, e. g. if we have the large I-shaped
tetris block, can this be styled individually too via gradient?
Since SWT probably allows most flexibility it may suffice if
the code could mention this perhaps? If it is too complicated
to support don't worry about it. I did not see colors at
https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/v4.18.3.1/samples/elaborate/tetris.rb
so I assume it is stored elsewhere.
I am sorry. This issue escaped me because I was busy with a work deadline when I received it and I forgot to respond to it afterwards.
The answer is definitely yes, but as a senior software developer, you should have figured out the answer for yourself. It is not something that is a big deal, and there are enough samples in Glimmer DSL for SWT to answer your own question.
In Glimmer DSL for SWT, there is a Tetris::View::Block
component that represents every tetromino square you see in Tetris's view, which is at:
https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tetris/view/block.rb
Notice how it has a base_color
property data-bound underneath bevel
:
require_relative 'bevel'
class Tetris
module View
class Block
include Glimmer::UI::CustomWidget
options :game_playfield, :block_size, :row, :column
body {
canvas { |canvas_proxy|
bevel(size: block_size) {
base_color <= [game_playfield[row][column], :color]
}
}
}
end
end
end
Specifically, this is the line responsible for giving blocks their color through unidirectional data-binding to the color of a row/column on the game playfield:
base_color <= [game_playfield[row][column], :color]
Now, the game playfield is made up of blocks, which get their colors from tetromino objects:
https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tetris/model/tetromino.rb
The only thing that matters to you is the color map at the top:
class Tetris
module Model
class Tetromino
ORIENTATIONS = [:north, :east, :south, :west]
LETTER_COLORS = {
I: :cyan,
J: :blue,
L: :dark_yellow,
O: :yellow,
S: :green,
T: :magenta,
Z: :red,
}
...
You can replace the static built-in colors (like :cyan
) with rgb arrays as follows:
class Tetris
module Model
class Tetromino
ORIENTATIONS = [:north, :east, :south, :west]
LETTER_COLORS = {
I: [120, 140, 200],
J: [0, 0, 255],
L: [0, 128, 128],
O: [0, 255, 255],
S: [0, 255, 0],
T: [255, 0, 255],
Z: [255, 0, 0],
}
Now, you get a differently colored Tetris (note that this screenshot also includes the second change mentioned below about adding a 1-pixel black border):
If you want to add a block border around the blocks, let's go back to Tetris::View::Block
, and we'll notice it is filling its canvas with bevel
components. These are at:
https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tetris/view/bevel.rb
The original bevel body is:
body {
rectangle(x, y, size, size) {
background <= [self, :base_color]
polygon(0, 0, size, 0, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
end
}]
}
polygon(size, 0, size - bevel_pixel_size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size, size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
end
}]
}
polygon(size, size, 0, size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
end
}]
}
polygon(0, 0, 0, size, bevel_pixel_size, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
end
}]
}
rectangle(0, 0, size, size) {
foreground <= [self, :base_color, on_read: ->(color_value) {
# use gray instead of white for the border
color_value == Model::Block::COLOR_CLEAR ? :gray : color_value
}]
}
}
}
In Glimmer DSL for SWT, you can take advantage of its relative positioning feature (not supported by other Glimmer toolkits yet) and actually nest everything inside a new rectangle that has a foreground color (meaning border color) of black:
body {
rectangle(x, y, size, size) {
foreground :black
rectangle(x + 1, y + 1, size - 2, size - 2) {
background <= [self, :base_color]
polygon(0, 0, size, 0, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
end
}]
}
polygon(size, 0, size - bevel_pixel_size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size, size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
end
}]
}
polygon(size, size, 0, size, bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size, size - bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
end
}]
}
polygon(0, 0, 0, size, bevel_pixel_size, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
background <= [self, :base_color, on_read: ->(color_value) {
unless color_value.nil?
color = color(color_value)
rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
end
}]
}
rectangle(0, 0, size, size) {
foreground <= [self, :base_color, on_read: ->(color_value) {
# use gray instead of white for the border
color_value == Model::Block::COLOR_CLEAR ? :gray : color_value
}]
}
}
}
}
Notice how we also shrunk the size of the second (inner) rectangle to compensate for the 1-pixel border:
rectangle(x + 1, y + 1, size - 2, size - 2) {
...
}
Now, Tetris has a block border around all the blocks.
If you want gradiants in Glimmer DSL for SWT, you simply copy the examples in hello_canvas.rb
by using background_pattern
instead of background
:
https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_canvas.rb
Unfortunately, backgorund_pattern
does not support data-binding out of the box (I added it to the TODO.md list) because it receives a compound value, not a single value, so you'd have to do manual data-binding using the observe
keyword.
Here is an example of re-writing the bevel
component to have a gradiant (data-bound in after_body
) instead of a bevel look (obviously in a real app, you'd have to rename the component to gradiant or something similar):
class Tetris
module View
class Bevel
include Glimmer::UI::CustomShape
options :base_color, :size, :bevel_pixel_size
option :x, default: 0
option :y, default: 0
before_body do
self.bevel_pixel_size = 0.16*size.to_f if bevel_pixel_size.nil?
end
after_body do
observe(self, :base_color) do |new_base_color|
new_base_color = color(new_base_color)
sum_of_colors = new_base_color.red + new_base_color.green + new_base_color.blue
gradiant_difference = sum_of_colors > 384 ? 150 : -150
new_base_color2_red = [[new_base_color.red + gradiant_difference, 255].min, 0].max
new_base_color2_green = [[new_base_color.green + gradiant_difference, 255].min, 0].max
new_base_color2_blue = [[new_base_color.blue + gradiant_difference, 255].min, 0].max
new_base_color2 = rgb(new_base_color2_red, new_base_color2_green, new_base_color2_blue)
body_root.content {
background_pattern 0, 0, size, size, new_base_color, new_base_color2
}
end
end
body {
rectangle(x, y, size, size)
}
end
end
end
This should address all your questions, so I am closing this.