From 30aece193be242948d5cc5a80e898357c45d3c5f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 5 Feb 2015 02:21:29 +0900 Subject: [PATCH] example/mapeditor: Draw map --- example/mapeditor/main.go | 10 ++- example/mapeditor/mapeditor/maineditor.go | 67 +++++++++++----- example/mapeditor/mapeditor/map.go | 89 ++++++++++++++++++++++ example/mapeditor/mapeditor/mapview.go | 72 +++++++++++++++++ example/mapeditor/mapeditor/panel.go | 59 ++++++++++++++ example/mapeditor/mapeditor/tileset.go | 16 ++-- example/mapeditor/mapeditor/tilesetview.go | 60 +++++++++++++++ 7 files changed, 343 insertions(+), 30 deletions(-) create mode 100644 example/mapeditor/mapeditor/map.go create mode 100644 example/mapeditor/mapeditor/mapview.go create mode 100644 example/mapeditor/mapeditor/panel.go create mode 100644 example/mapeditor/mapeditor/tilesetview.go diff --git a/example/mapeditor/main.go b/example/mapeditor/main.go index 2c88eda2b..4ee9b4097 100644 --- a/example/mapeditor/main.go +++ b/example/mapeditor/main.go @@ -35,7 +35,13 @@ func init() { panic(err) } tileSet := mapeditor.NewTileSet(tileSetImg) - editor = mapeditor.NewMainEditor(tileSet) + + m := mapeditor.NewMap(20, 15) + + editor, err = mapeditor.NewMainEditor(tileSet, m) + if err != nil { + panic(err) + } } func update(screen *ebiten.Image) error { @@ -43,7 +49,7 @@ func update(screen *ebiten.Image) error { return err } - backgroundColor := color.RGBA{0x80, 0x80, 0x80, 0xff} + backgroundColor := color.RGBA{0xc0, 0xc0, 0xc0, 0xff} screen.Fill(backgroundColor) return editor.Draw(screen) } diff --git a/example/mapeditor/mapeditor/maineditor.go b/example/mapeditor/mapeditor/maineditor.go index a84189939..6a954baa7 100644 --- a/example/mapeditor/mapeditor/maineditor.go +++ b/example/mapeditor/mapeditor/maineditor.go @@ -18,37 +18,62 @@ import ( "github.com/hajimehoshi/ebiten" ) +const ( + tileSetX = 16 + tileSetY = 16 + tileSetWidth = TileSetXNum * TileWidth + tileSetHeight = TileSetYNum * TileHeight + mapViewX = 16 + TileWidth*TileSetXNum + 16 + mapViewY = 16 + mapViewWidth = 720 + mapViewHeight = 720 +) + type MainEditor struct { - tileSet *TileSet - tileSetX int - tileSetY int - selectedTile int + tileSetView *TileSetView + tileSetPanel *Panel + mapView *MapView + mapPanel *Panel + mapCursorX int + mapCursorY int } -func NewMainEditor(tileSet *TileSet) *MainEditor { - return &MainEditor{ - tileSet: tileSet, - tileSetX: 16, - tileSetY: 16, +func NewMainEditor(tileSet *TileSet, m *Map) (*MainEditor, error) { + tileSetView := NewTileSetView(tileSet) + tileSetPanel, err := NewPanel(tileSetView, tileSetX, tileSetY, tileSetWidth, tileSetHeight) + if err != nil { + return nil, err } + mapView := NewMapView(m) + mapPanel, err := NewPanel(mapView, mapViewX, mapViewY, mapViewWidth, mapViewHeight) + if err != nil { + return nil, err + } + return &MainEditor{ + tileSetView: tileSetView, + tileSetPanel: tileSetPanel, + mapView: mapView, + mapPanel: mapPanel, + }, nil } func (m *MainEditor) Update() error { - if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { - x, y := ebiten.CursorPosition() - x -= m.tileSetX - y -= m.tileSetY - if 0 <= x && 0 <= y && x < TileWidth*TileSetXNum && y < TileHeight*TileSetYNum { - tile, err := m.tileSet.TileAt(x, y) - if err != nil { - return err - } - m.selectedTile = tile - } + if err := m.tileSetView.Update(tileSetX, tileSetY); err != nil { + return err + } + tileSet := m.tileSetView.tileSet + if err := m.mapView.Update(mapViewX, mapViewY, mapViewWidth, mapViewHeight, tileSet, m.tileSetView.selectedTile); err != nil { + return err } return nil } func (m *MainEditor) Draw(screen *ebiten.Image) error { - return m.tileSet.Draw(screen, m.selectedTile, m.tileSetX, m.tileSetY) + if err := m.tileSetPanel.Draw(screen); err != nil { + return err + } + if err := m.mapPanel.Draw(screen); err != nil { + return err + } + return nil } diff --git a/example/mapeditor/mapeditor/map.go b/example/mapeditor/mapeditor/map.go new file mode 100644 index 000000000..4586c0cf7 --- /dev/null +++ b/example/mapeditor/mapeditor/map.go @@ -0,0 +1,89 @@ +// Copyright 2015 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapeditor + +import ( + "github.com/hajimehoshi/ebiten" + "image/color" +) + +type Map struct { + width int + height int + tiles []int +} + +func NewMap(width, height int) *Map { + return &Map{ + width: width, + height: height, + tiles: make([]int, width*height), + } +} + +func (m *Map) TileAt(x, y int) (int, error) { + /*if x < 0 || y < 0 || TileWidth*TileSetXNum <= x || TileHeight*TileSetYNum <= y { + return 0, fmt.Errorf("out of range: (%d, %d)", x, y) + }*/ + return x/TileWidth + y/TileHeight, nil +} + +func (m *Map) SetTile(x, y int, tile int) { + i := x + y*m.width + m.tiles[i] = tile +} + +type TileRects struct { + m *Map +} + +func (t *TileRects) Len() int { + return t.m.width * t.m.height +} + +func (t *TileRects) Src(i int) (x0, y0, x1, y1 int) { + tile := t.m.tiles[i] + x := tile % TileSetXNum * TileLogicWidth + y := tile / TileSetXNum * TileLogicHeight + return x, y, x + TileLogicWidth, y + TileLogicHeight +} + +func (t *TileRects) Dst(i int) (x0, y0, x1, y1 int) { + x := i % t.m.width * TileLogicWidth + y := i / t.m.width * TileLogicHeight + return x, y, x + TileLogicWidth, y + TileLogicHeight +} + +func (m *Map) Draw(i *ebiten.Image, tileSetImg *ebiten.Image, x, y int) error { + i.Fill(color.RGBA{0x80, 0x80, 0x80, 0xff}) + + op := &ebiten.DrawImageOptions{ + ImageParts: &TilesBackgroundRects{m.width, m.height}, + } + op.GeoM.Translate(float64(x), float64(y)) + if err := i.DrawImage(tilesBackground, op); err != nil { + return err + } + + op = &ebiten.DrawImageOptions{ + ImageParts: &TileRects{m}, + } + op.GeoM.Translate(float64(x), float64(y)) + op.GeoM.Scale(2, 2) + if err := i.DrawImage(tileSetImg, op); err != nil { + return err + } + return nil +} diff --git a/example/mapeditor/mapeditor/mapview.go b/example/mapeditor/mapeditor/mapview.go new file mode 100644 index 000000000..6342c947a --- /dev/null +++ b/example/mapeditor/mapeditor/mapview.go @@ -0,0 +1,72 @@ +// Copyright 2015 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapeditor + +import ( + "github.com/hajimehoshi/ebiten" + "image/color" +) + +type MapView struct { + m *Map + tileSet *TileSet + cursorX int + cursorY int +} + +func NewMapView(m *Map) *MapView { + return &MapView{ + m: m, + cursorX: -1, + cursorY: -1, + } +} + +func (m *MapView) Update(ox, oy, width, height int, tileSet *TileSet, selectedTile int) error { + m.tileSet = tileSet + + x, y := ebiten.CursorPosition() + x -= ox + y -= oy + if x < 0 || y < 0 || width <= x || height <= y { + return nil + } + if m.m.width*TileWidth <= x || m.m.height*TileHeight <= y { + return nil + } + m.cursorX = x / TileWidth + m.cursorY = y / TileHeight + if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { + m.m.SetTile(m.cursorX, m.cursorY, selectedTile) + } + return nil +} + +func (m *MapView) Draw(i *ebiten.Image, x, y, width, height int) error { + if err := m.m.Draw(i, m.tileSet.image, x, y); err != nil { + return err + } + + if m.cursorX == -1 || m.cursorY == -1 { + return nil + } + sx := x + m.cursorX*TileWidth + sy := y + m.cursorY*TileHeight + i.DrawRect(sx, sy, TileWidth, TileHeight, color.Black) + i.DrawRect(sx+1, sy+1, TileWidth-2, TileHeight-2, color.White) + i.DrawRect(sx+2, sy+2, TileWidth-4, TileHeight-4, color.Black) + + return nil +} diff --git a/example/mapeditor/mapeditor/panel.go b/example/mapeditor/mapeditor/panel.go new file mode 100644 index 000000000..d79d4ad60 --- /dev/null +++ b/example/mapeditor/mapeditor/panel.go @@ -0,0 +1,59 @@ +// Copyright 2015 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapeditor + +import ( + "github.com/hajimehoshi/ebiten" +) + +type drawer interface { + Draw(target *ebiten.Image, x, y, width, height int) error +} + +type Panel struct { + drawer drawer + panelX int + panelY int + regionX int + regionY int + width int + height int + offscreen *ebiten.Image +} + +func NewPanel(drawer drawer, x, y, width, height int) (*Panel, error) { + offscreen, err := ebiten.NewImage(width, height, ebiten.FilterNearest) + if err != nil { + return nil, err + } + return &Panel{ + drawer: drawer, + panelX: x, + panelY: y, + width: width, + height: height, + offscreen: offscreen, + }, nil +} + +func (p *Panel) Draw(target *ebiten.Image) error { + if err := p.drawer.Draw(p.offscreen, p.regionX, p.regionY, p.width, p.height); err != nil { + return err + } + + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(float64(p.panelX), float64(p.panelY)) + return target.DrawImage(p.offscreen, op) +} diff --git a/example/mapeditor/mapeditor/tileset.go b/example/mapeditor/mapeditor/tileset.go index 07c9cfd3e..db452c2fa 100644 --- a/example/mapeditor/mapeditor/tileset.go +++ b/example/mapeditor/mapeditor/tileset.go @@ -21,10 +21,12 @@ import ( ) const ( - TileWidth = 32 - TileHeight = 32 - TileSetXNum = 8 - TileSetYNum = 16 + TileLogicWidth = 16 + TileLogicHeight = 16 + TileWidth = 32 + TileHeight = 32 + TileSetXNum = 8 + TileSetYNum = 16 ) var tilesBackground *ebiten.Image @@ -82,7 +84,7 @@ func (t *TileSet) TileAt(x, y int) (int, error) { return x/TileWidth + y/TileHeight*TileSetXNum, nil } -func (t *TileSet) Draw(i *ebiten.Image, s int, x, y int) error { +func (t *TileSet) Draw(i *ebiten.Image, x, y, width, height int) error { op := &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(x), float64(y)) op.ImageParts = &TilesBackgroundRects{8, 16} @@ -97,11 +99,11 @@ func (t *TileSet) Draw(i *ebiten.Image, s int, x, y int) error { return err } - sx := x + s%TileSetXNum*TileWidth + /*sx := x + s%TileSetXNum*TileWidth sy := y + s/TileSetXNum*TileHeight i.DrawRect(sx, sy, TileWidth, TileHeight, color.Black) i.DrawRect(sx+1, sy+1, TileWidth-2, TileHeight-2, color.White) - i.DrawRect(sx+2, sy+2, TileWidth-4, TileHeight-4, color.Black) + i.DrawRect(sx+2, sy+2, TileWidth-4, TileHeight-4, color.Black)*/ return nil } diff --git a/example/mapeditor/mapeditor/tilesetview.go b/example/mapeditor/mapeditor/tilesetview.go new file mode 100644 index 000000000..3773159cd --- /dev/null +++ b/example/mapeditor/mapeditor/tilesetview.go @@ -0,0 +1,60 @@ +// Copyright 2015 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapeditor + +import ( + "github.com/hajimehoshi/ebiten" + "image/color" +) + +type TileSetView struct { + tileSet *TileSet + selectedTile int +} + +func NewTileSetView(tileSet *TileSet) *TileSetView { + return &TileSetView{ + tileSet: tileSet, + } +} + +func (t *TileSetView) Update(ox, oy int) error { + if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { + x, y := ebiten.CursorPosition() + x -= ox + y -= oy + if 0 <= x && 0 <= y && x < TileWidth*TileSetXNum && y < TileHeight*TileSetYNum { + tile, err := t.tileSet.TileAt(x, y) + if err != nil { + return err + } + t.selectedTile = tile + } + } + return nil +} + +func (t *TileSetView) Draw(i *ebiten.Image, x, y, width, height int) error { + t.tileSet.Draw(i, x, y, width, height) + + s := t.selectedTile + sx := x + s%TileSetXNum*TileWidth + sy := y + s/TileSetXNum*TileHeight + i.DrawRect(sx, sy, TileWidth, TileHeight, color.Black) + i.DrawRect(sx+1, sy+1, TileWidth-2, TileHeight-2, color.White) + i.DrawRect(sx+2, sy+2, TileWidth-4, TileHeight-4, color.Black) + + return nil +}