1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ebitenで点滅するウィンドウをタッチ操作で選択してみたの

Last updated at Posted at 2018-08-08

タッチ操作で複数のウィンドウから1つ選ぶ

image.png

今回は2つの取組みを紹介します٩( 'ω' )و

  • 枠が点滅するウィンドウの実装
  • 複数あるウィンドウをタッチ操作で選択して、選択したウィンドウだけ枠を点滅させる

解説に使うコードは以下に置いています。

kemokemo/ebiten-sketchbook - blink-framewindow
kemokemo/ebiten-sketchbook - select-one

使用環境

実装内容の説明

点滅機能付きのウィンドウFrameWindowの説明

枠付きのウィンドウ

実はとても単純な仕組みです。下図をご覧ください。

image.png

  • 枠の内側となる画像innerImg
  • 上記よりも上下左右に枠の幅frameWidthの分だけ大きい画像frameImg

これらの画像を作って、innerImgを上に重ねて描画するだけです。

frame_window.go

frame_window.go
// NewFrameWindow returns a FrameWindow.
//
// The width and height are used for the inner region excluding the frame.
// If 0 is set to the frameWidth, the frame will not be drawn.
func NewFrameWindow(x, y, width, height, frameWidth int) (*FrameWindow, error) {
	fw := FrameWindow{
		rect: image.Rectangle{
			Min: image.Point{X: x, Y: y},
			Max: image.Point{X: x + width, Y: y + height},
		},
	}
	var err error
	fw.innerImg, err = ebiten.NewImage(width, height, ebiten.FilterDefault)
	if err != nil {
		return nil, err
	}
	err = fw.innerImg.Fill(color.White)
	if err != nil {
		return nil, err
	}
	fw.innerOp = &ebiten.DrawImageOptions{}
	fw.innerOp.GeoM.Translate(float64(x), float64(y))

	if frameWidth > 0 {
		fw.frameImg, err = ebiten.NewImage(width+frameWidth*2, height+frameWidth*2, ebiten.FilterDefault)
		if err != nil {
			return nil, err
		}
		err = fw.frameImg.Fill(color.White)
		if err != nil {
			return nil, err
		}
		fw.frameDarkOp = &ebiten.DrawImageOptions{}
		fw.frameDarkOp.GeoM.Translate(float64(x-frameWidth), float64(y-frameWidth))
		fw.frameLightOp = &ebiten.DrawImageOptions{}
		fw.frameLightOp.GeoM.Translate(float64(x-frameWidth), float64(y-frameWidth))
	}
	return &fw, nil
}

枠を点滅させる

image.png

ebitenで画像を描画する時にはDrawImageOptionsを指定しますが、枠の描画に使うDrawImageOptionsに明るいframeLightOpと暗いframeDarkOpを用意しておいて、frameImgの描画に使うDrawImageOptionsを時間経過で変更すれば点滅動作となります。やったね!٩( 'ω' )و

FrameWindowの使い方

動作イメージ

実際に使ってみるとこんな感じになります。

blink_frame-window.gif

インスタンスの生成と色や点滅の設定

var err error
frameWindow, err = ui.NewFrameWindow(20, 10, 100, 120, 5)
if err != nil {
  return err
}

frameWindow.SetColors(
  color.RGBA{64, 64, 64, 255},
  color.RGBA{192, 192, 192, 255},
  color.RGBA{0, 148, 255, 255})
frameWindow.SetBlink(true)

SetColorsでは引数で、ウィンドウ内側枠の暗い時の色枠の明るい時の色を順に指定します。

frame_window.go
// SetColors sets the colors of the window's inner region and the frame's
// normal color.
// If you need to blink the frame, please use the SetBlinkFrame method.
func (w *FrameWindow) SetColors(inner, frameDark, frameLight color.RGBA) {
	w.innerOp.ColorM.Scale(colorScale(inner))

以下省略

SetBlinktrueを渡せば点滅On、点滅させないときはfalseを渡します。

描画する

あとは、ebiten.Runに第一引数で渡すコールバック関数、例えば以下のようなupdateメソッドなど定期的に描画の更新を行う処理でFrameWindowDrawWindowメソッドを実行するだけです。

func update(screen *ebiten.Image) error {
	if ebiten.IsRunningSlowly() {
		return nil
	}

	frameWindow.DrawWindow(screen)
	return nil
}

タッチ位置とウィンドウの領域の重なりを検知する

ここまでに説明してきたFrameWindowは、複数表示してキャラクター選択画面アイテム選択画面などで使うことをイメージしています。これをタッチ操作で選択したいと思います。

前回の記事の試作でタッチ位置は取得可能になっていますので、後は「タッチした位置とウィンドウ描画領域の重なりの有無」が分かればタッチされたウィンドウが判別できそうです。

実は上記で説明したNewFrameWindowメソッドの冒頭で、枠を含まないウィンドウ領域を算出しておりGetWindowRectメソッドで取得できる仕組みにしてあります。

frame_window.go
// GetWindowRect returns the rectangle of this window.
func (w *FrameWindow) GetWindowRect() image.Rectangle {
	return w.rect
}

そこで、ユーザーが最初にタッチした位置引数で渡した領域に含まれるか否かbool値で返してくれるメソッドを作りました。FrameWindowGetWindowRectから取得したimage.Rectangleを渡せば、タッチされたウィンドウが判別できます。

input.go

input.go
// IsTouched returns the state that touched or not.
func IsTouched(r image.Rectangle) bool {
	IDs := ebiten.TouchIDs()
	if len(IDs) == 0 {
		return false
	}

	// use only the first touched point
	sort.Slice(IDs, func(i, j int) bool {
		return IDs[i] < IDs[j]
	})
	x, y := ebiten.TouchPosition(IDs[0])

	min := r.Min
	max := r.Max
	if min.X < x && x < max.X && min.Y < y && y < max.Y {
		return true
	}
	return false
}

タッチされたウィンドウだけ点滅させる

後は「タッチされたよ!」とわかったウィンドウに対してSetBlink(true)を実行して描画すればおkです。
次回はスワイプ操作のハンドリングとかやってみたい!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?