画面作成のためのKotlin入門~Kotlinの基本を学習する~

イントロダクション

Kotlinで画面の作製、GUIを作り、ゲームを作成しようと思っています。
KorGEというゲームエンジンをインストールして、サンプルプロジェクトを動かしたところで「基本がわからん!」となり、学習することにしました。

学習したいポイントは以下の通りです。

  1. 基本文法
  2. ライブラリなどのインポート方法
  3. GUIの作り方

まだまだ、基本の学習中でコードの解析が終わらないので引き続きこの記事は更新いたします。

問題1

Kotlinは開発環境にすごーく依存するため、ライブラリなどGradleでの設定が必要になるので。。。

解決策

サンプルプロジェクトをカスタムする方向でプログラムを学習&作成していくことにしました。

参考サイト

  1. korlibs.kogeパッケージのAPIドキュメント
  2. KorGEのサイト
  3. KorGEのドキュメントサイト

今回は、GUIアプリを作成したいので、サンプルに「2048」というゲームをカスタムします。
2048ゲームの画面

Kotlinの基本

まずは、サンプルコードを見てみます。結構長いです。。。
そのため、上記のリンク先にコードがあります。

基本を学習するにあたり、サンプルコードを見ながら「ここは、こーなっている。。。」という形で学習していきます。

import文について

サンプルコードの下のようなコードがこれに当たります。

import korlibs.event.*
import korlibs.korge.*
import korlibs.korge.animate.*
 ・
 ・
 ・

各ライブラリのクラスをインポートしています。Javaと同じです。
そして、自分で作成したクラスなどもインポートできます。

ちなみにJava言語での「static import」が使えるかについては「NO」通常のimport文でインポートできるようです。使えるようにできるということです。
stackoverfllow参照

変数の扱い

下のように定義できます。

// 定数
val x: Int = 5
// 変更可能な普通の変数
var y: Int = 5
// インクリメント
y += 1

省略した書き方

val x = 5

関数と変数の定義

val PI = 3.14
var x = 0

fun incrementX() {
    x += 1
}

varとvalの違い

サンプルコードには下のようなコードがあります。「var」と「val」、そして「fun」で始まる行があります。
結論から言うとJava言語でいうなら

  • 「val」は「final」修飾子つきの変数です。
    つまり定数です。クラスの場合は、インスタンスが変更されなければエラーになりません。
  • 「var」は通常の変数です。
    つまり、値の書き換えなど変更ができます。
  • 「fun」は関数を示す。
    fun numberFor(blockId: Int) = blocks[blockId]!!.number

    上の関数は、blocks配列のblockId番目のnumberを返します。

ちなみに、サンプルコードの一部を抜粋、読んで理解しているところです。

var cellSize: Float = 0f
var fieldSize: Float = 0f
var leftIndent: Float = 0f
var topIndent: Float = 0f
var font: BitmapFont by Delegates.notNull()

fun columnX(number: Int) = leftIndent + 10 + (cellSize + 10) * number
fun rowY(number: Int) = topIndent + 10 + (cellSize + 10) * number

var map = PositionMap()
val blocks = mutableMapOf<Int, Block>()
var history: History by Delegates.notNull()

fun numberFor(blockId: Int) = blocks[blockId]!!.number
fun deleteBlock(blockId: Int) = blocks.remove(blockId)!!.removeFromParent()

val score = ObservableProperty(0)
val best = ObservableProperty(0)

var freeId = 0
var isAnimationRunning = false
var isGameOver = false

文字列の扱い

変数と文字列を同時に使用する場合、Java言語で書くと右のような書き方「println("XXX" + a)」の処理を行いたい場合

var a = 1
// simple name in template:
val s1 = "a is $a" 

a = 2
// arbitrary expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"

「$」を使用するようです。どこかのフレームワークで見たような。。。感じです。
注意点として、Stringクラスのメソッドを使用したい時などは「{}」で囲う必要があるみたいです。

クラスの作り方

コンストラクタで作成する感じ?

class Rectangle(val height: Double, val length: Double) {
    val perimeter = (height + length) * 2 
}
fun main() {
    val rectangle = Rectangle(5.0, 2.0)
    println("The perimeter is ${rectangle.perimeter}")
}

継承関係の作り方「:」を使用する。Shapeクラスを継承したRectangleクラス

open class Shape

class Rectangle(val height: Double, val length: Double): Shape() {
    val perimeter = (height + length) * 2
}

メインメソッド

引き続き読み進めていきます。次はメインメソッドを読んでいきます。ちょっと長いので一部抜粋しながら読み進めます。

まずは、下のコードです。メインメソッドの書き始め部分です。通常のメインメソッドは下のように書くのですが。。。

fun main(args:Array<String>){
    println("Hello Kotlin")
}

もしくは、下のように書きます。

fun main() {
    println("Hello world!")
}

標準入出力

標準入力は、ユーザーの入力、出力はコンソールなどに出力することを言います。
Kotlinの場合は簡単に記述できます。

println("入力してください: ")

// ユーザーの入力を受け付けます
val yourWord = readln()

print("標準出力に入力した文字を出力: ")
print(yourWord)

関数(Function)の定義方法

Kotlinで定義する場合の書き方です。それぞれ引数あり、なし、返り値あり、なしの書き方を記載します。

引数と返り値ありの関数

引数あり、返り値ありの関数。引数も返り値もInt型です。

fun sum(a: Int, b: Int): Int {
    return a + b
}

簡単な課書き方をすると下のようになります。

fun sum(a: Int, b: Int) = a + b

引数あり、返り値なしの関数

引数あり、返り値なしの場合です。

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

引数なし、返り値なしの関数

fun printSum() {
    println("sum of $a and $b is " + (1 + 2))
}

KorGEの場合

KorGEでは違うようです。これもひとつずつ分解指定解析します。1回解析してしまえばあとは、見ただけでわかります(笑)

初めの疑問点

メインメソッドの「suspend fun main()」の「suspend」の部分です。どんな意味があるのでしょうか?

suspend fun main() = Korge(
    virtualSize = Size(480, 640),
    title = "2048",
    bgcolor = RGBA(253, 247, 240),
    /**
        `gameId` is associated with the location of storage, which contains `history` and `best`.
        see [Views.realSettingsFolder]
     */
    gameId = "io.github.rezmike.game2048",
    forceRenderEveryFrame = false, // Optimization to reduce battery usage!
) { ... }

ここでの注意点は「suspend」キーワードです。このキーワードはこちらのページを参考に学習しました。またこちらのページでは「Coroutineとsuspendの違い」について学習しました。

結論を書くと下のようになります。ChatGPT先生に聞いたら簡潔に答えてくれました。

  • Coroutineは非同期処理全体を管理するためのメカニズム
  • suspend関数(修飾子?)はCoroutine内で一時停止可能な関数であり、非同期処理の具体的な一部を担います。

つまりsuspendは

「非同期処理とか、一時停止可能な関数(function)ですよ」という意味でした。
具体的には、現在のアプリが動いているデバイス以外へリクエストなどを送信しても、結果が返ってくるのを待たずに処理が動くとか
レスポンスを待って処理を行うとかが可能という意味でス。

「どうやって?」という部分に関しては今後調査していく必要があります。しかし、現段階ではここでSTOPしておきます。

次の疑問点

「main() = XXX」の部分です。具体的には、下の部分です。

fun main() = Korge(
    virtualSize = Size(480, 640),
    title = "2048",
    bgcolor = RGBA(253, 247, 240),
    /**
        `gameId` is associated with the location of storage, which contains `history` and `best`.
        see [Views.realSettingsFolder]
     */
    gameId = "io.github.rezmike.game2048",
    forceRenderEveryFrame = false, // Optimization to reduce battery usage!
)

こちらのサイトを参考にすると「インライン関数」として書いてあるようですが、ちょっと違うような気がします。

次いで調べたのがKotlinのドキュメントページです、英語ですが、日本語に変換して読みます。
Kotlin-Flllowを使用してデバックする」という記事にありました。
コードとしては、下のようなコードで、「メソッドの内容を書き換える」書き方です。。。

fun main() = runBlocking {
    simple()
        .collect { value ->
            delay(300)
            println(value)
        }
}

上記のコードは、下のコードをデバックするためのコードのようです。
つまり、メインメソッドにブロックを使用しrunBlocking()メソッドでコルーチンをラップします。
事前に定義されているのが「runBlocking()」でコルーチンの仕組みの一つです。
このメソッドをオーバーライドしてデバックを行っているというわけですね。なるほど!

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlin.system.*

fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        emit(i)
    }
}

結局は。。。

問題の「main() = KorGE( ... )の部分はKorGEフレームワークでメインメソッドを書き換えているということで、納得しました。

つまりは、上のrunBlocking()関数で書き換えたときと同じように、KorGEクラスで書き換えた、オーバーライドしてメインの処理を実行するようにした
ということです。
そのため、下のようにコンストラクタ部分に、画面の情報をセットしています。

Korge(
    virtualSize = Size(480, 640),
    title = "2048",
    bgcolor = RGBA(253, 247, 240),
    ・
    ・
    ・

そして、処理(main)部分がそのあとに続きます。

suspend main() = KorgGE(
    ... // 画面サイズなどのプロパティを設定
) {
    メインの処理。。。
}

サンプルコード

Kotlinの基本に戻る

import korlibs.event.*
import korlibs.korge.*
import korlibs.korge.animate.*
import korlibs.korge.input.*
import korlibs.korge.service.storage.*
import korlibs.korge.tween.*
import korlibs.korge.ui.*
import korlibs.korge.view.*
import korlibs.image.color.*
import korlibs.image.font.*
import korlibs.image.format.*
import korlibs.image.text.TextAlignment
import korlibs.io.async.*
import korlibs.io.async.ObservableProperty
import korlibs.io.file.std.*
import korlibs.korge.style.styles
import korlibs.korge.style.textColor
import korlibs.korge.style.textFont
import korlibs.korge.style.textSize
import korlibs.korge.view.align.*
import korlibs.math.geom.*
import korlibs.math.interpolation.*
import korlibs.time.seconds
import kotlin.collections.set
import kotlin.properties.*
import kotlin.random.*

var cellSize: Float = 0f
var fieldSize: Float = 0f
var leftIndent: Float = 0f
var topIndent: Float = 0f
var font: BitmapFont by Delegates.notNull()

fun columnX(number: Int) = leftIndent + 10 + (cellSize + 10) * number
fun rowY(number: Int) = topIndent + 10 + (cellSize + 10) * number

var map = PositionMap()
val blocks = mutableMapOf<Int, Block>()
var history: History by Delegates.notNull()

fun numberFor(blockId: Int) = blocks[blockId]!!.number
fun deleteBlock(blockId: Int) = blocks.remove(blockId)!!.removeFromParent()

val score = ObservableProperty(0)
val best = ObservableProperty(0)

var freeId = 0
var isAnimationRunning = false
var isGameOver = false

suspend fun main() = Korge(
    virtualSize = Size(480, 640),
    title = "2048",
    bgcolor = RGBA(253, 247, 240),
    /**
        `gameId` is associated with the location of storage, which contains `history` and `best`.
        see [Views.realSettingsFolder]
     */
    gameId = "io.github.rezmike.game2048",
    forceRenderEveryFrame = false, // Optimization to reduce battery usage!
) {
    font = resourcesVfs["clear_sans.fnt"].readBitmapFont()

    val storage = views.storage
    history = History(storage.getOrNull("history")) {
        storage["history"] = it.toString()
    }
    best.update(storage.getOrNull("best")?.toInt() ?: 0)

    score.observe {
        if (it > best.value) best.update(it)
    }
    best.observe {
        storage["best"] = it.toString()
    }

    cellSize = views.virtualWidth / 5f
    fieldSize = 50 + 4 * cellSize
    leftIndent = (views.virtualWidth - fieldSize) / 2
    topIndent = 150f

    val bgField = roundRect(Size(fieldSize, fieldSize), RectCorners(5), fill = Colors["#b9aea0"]) {
        position(leftIndent, topIndent)
    }
    graphics {
        fill(Colors["#cec0b2"]) {
            for (i in 0..3) {
                for (j in 0..3) {
                    roundRect(
                        10 + (10 + cellSize) * i, 10 + (10 + cellSize) * j,
                        cellSize, cellSize,
                        5f
                    )
                }
            }
        }
    }.position(leftIndent, topIndent)

    val bgLogo = roundRect(Size(cellSize, cellSize), RectCorners(5), fill = RGBA(237, 196, 3)) {
        position(leftIndent, 30f)
    }
    text("2048", cellSize * 0.5f, Colors.WHITE, font).centerOn(bgLogo)

    val bgBest = roundRect(Size(cellSize * 1.5, cellSize * 0.8), RectCorners(5f), fill = Colors["#bbae9e"]) {
        alignRightToRightOf(bgField)
        alignTopToTopOf(bgLogo)
    }
    text("BEST", cellSize * 0.25f, RGBA(239, 226, 210), font) {
        centerXOn(bgBest)
        alignTopToTopOf(bgBest, 5.0)
    }
    text(best.value.toString(), cellSize * 0.5f, Colors.WHITE, font) {
        setTextBounds(Rectangle(0f, 0f, bgBest.width, cellSize - 24f))
        alignment = TextAlignment.MIDDLE_CENTER
        alignTopToTopOf(bgBest, 12.0)
        centerXOn(bgBest)
        best.observe {
            text = it.toString()
        }
    }

    val bgScore = roundRect(Size(cellSize * 1.5f, cellSize * 0.8f), RectCorners(5.0f), fill = Colors["#bbae9e"]) {
        alignRightToLeftOf(bgBest, 24.0)
        alignTopToTopOf(bgBest)
    }
    text("SCORE", cellSize * 0.25f, RGBA(239, 226, 210), font) {
        centerXOn(bgScore)
        alignTopToTopOf(bgScore, 5.0)
    }
    text(score.value.toString(), cellSize * 0.5f, Colors.WHITE, font) {
        setTextBounds(Rectangle(0f, 0f, bgScore.width, cellSize - 24f))
        alignment = TextAlignment.MIDDLE_CENTER
        centerXOn(bgScore)
        alignTopToTopOf(bgScore, 12.0)
        score.observe {
            text = it.toString()
        }
    }

    val btnSize = cellSize * 0.3
    val restartImg = resourcesVfs["restart.png"].readBitmap()
    val undoImg = resourcesVfs["undo.png"].readBitmap()
    val restartBlock = container {
        val background = roundRect(Size(btnSize, btnSize), RectCorners(5f), fill = RGBA(185, 174, 160))
        image(restartImg) {
            size(btnSize * 0.8, btnSize * 0.8)
            centerOn(background)
        }
        alignTopToBottomOf(bgBest, 5.0)
        alignRightToRightOf(bgField)
        onClick {
            this@Korge.restart()
        }
    }
    val undoBlock = container {
        val background = roundRect(Size(btnSize, btnSize), RectCorners(5f), fill = RGBA(185, 174, 160))
        image(undoImg) {
            size(btnSize * 0.6, btnSize * 0.6)
            centerOn(background)
        }
        alignTopToTopOf(restartBlock)
        alignRightToLeftOf(restartBlock, 5.0)
        onClick {
            this@Korge.restoreField(history.undo())
        }
    }

    if (!history.isEmpty()) {
        restoreField(history.currentElement)
    } else {
        generateBlockAndSave()
    }

    root.keys.down {
        when (it.key) {
            Key.LEFT -> moveBlocksTo(Direction.LEFT)
            Key.RIGHT -> moveBlocksTo(Direction.RIGHT)
            Key.UP -> moveBlocksTo(Direction.TOP)
            Key.DOWN -> moveBlocksTo(Direction.BOTTOM)
            else -> Unit
        }
    }

    onSwipe(20.0) {
        when (it.direction) {
            SwipeDirection.LEFT -> moveBlocksTo(Direction.LEFT)
            SwipeDirection.RIGHT -> moveBlocksTo(Direction.RIGHT)
            SwipeDirection.TOP -> moveBlocksTo(Direction.TOP)
            SwipeDirection.BOTTOM -> moveBlocksTo(Direction.BOTTOM)
        }
    }
}

Kotolinでゲーム開発 KorgeForgeをインストール~簡単に作ってみよう

Korge ForgeでAndroidアプリの開発

Kotlinというプログミング言語を使用して、Androidアプリが作れます。
しかし、知らない言語というのはプログラミングの初心者、玄人ともに、初めてなわけで。。。

とっかかりが難しい、というのが通常ですが、Kotolinはちがうのです!そしてKorgeForgeを使えばさらに違いが判る!!

イントロダクション

KotlinというのはJetBrainという会社が作成した プログラミング言語です。
Java言語とも連携が可能です。シンプルにKotlinからJavaのクラスを呼び出すこともできます。

なぜKORGE FORGE?

単純にシンプル(簡単)でゲームを作成するのに便利かつ強力だからお勧めします。
そして、プログラミング初心者でもわかりやすいようにチュートリアルなどもあります。

ただし、英語なんですね。。。

この記事の内容

ズバリ、上記のチュートリアルの内容を筆者が一生懸命理解した内容を記述します。内容としては以下の通りです。

  1. TUTORIAL1: インストール
  2. TUTORIAL2: 図形を描画しよう

しかし、Kotlinの基本がわからないので、躓いております、
そのため、こちらの記事でKotlinの基本を学習中です。

KORGE FORGEについて

KORGE FORGEはIDE(開発ツール)です。位置付けはゲームエンジンですが。。。以前はプラグインになっていましたが、たぶんライブラリなどがアップグレードされるたびに、ドキュメントのメンテナンスなどがてまになり。。。KORGE FORGEになったと筆者が思っています。
つまりは、簡単にプログラムを書いて動かせるということです。

TUTORIAL1: インストール

KORGE FORGEのインストール方法について学習している動画でした。
内容に関しては、以下の通りです。

  1. KORGE FORGEのサイトからコマンドでインストールできる
  2. インストールしたら、インストール画面から依存関係の追加もできる

とりあえずKORGE FORGEをインストールするところまででした。

以前のものでは、IntelliJを使用使用していましたが、依存関係の修正などが手間になったと思われます※筆者が勝手にそう思いました。

そんなわけで、筆者もやってみました。

インストール(Windows)

サンプルアプリ起動(#2に当たる?)

よくある話ですが、プログラムを起動する前に依存関係を追加したり色々やったりしていることが多いですが、今回は何もありませんでした。

つまりは、インストール後にすぐ動かした状態です。

初めのコード

TUTORIAL1のコードは下のようになっています。クラス名が「シーン」となっているのでコメントにはシーンと記述していますが、筆者もこれから学習していきます。

とりあえずは、メインメソッドは「fun main()」で記述するという所を学習しました。

import korlibs.time.*
import korlibs.korge.*
import korlibs.korge.scene.*
import korlibs.korge.tween.*
import korlibs.korge.view.*
import korlibs.image.color.*
import korlibs.image.format.*
import korlibs.io.file.std.*
import korlibs.math.geom.*
import korlibs.math.interpolation.*

/** メインメソッド */
suspend fun main() = Korge(windowSize = Size(512, 512), backgroundColor = Colors["#2b2b2b"]) {
    val sceneContainer = sceneContainer()

    sceneContainer.changeTo { MyScene() }
}
/** シーンを描画するクラス */
class MyScene : Scene() {
    override suspend fun SContainer.sceneMain() {
        val minDegrees = (-16).degrees
        val maxDegrees = (+16).degrees

        val image = image(resourcesVfs["korge.png"].readBitmap()) {
            rotation = maxDegrees
            anchor(.5, .5)
            scale(0.8)
            position(256, 256)
        }

        while (true) {
            image.tween(image::rotation[minDegrees], time = 1.seconds, easing = Easing.EASE_IN_OUT)
            image.tween(image::rotation[maxDegrees], time = 1.seconds, easing = Easing.EASE_IN_OUT)
        }
    }
}

メインメソッドの処理内容

メインメソッドのコードは下の部分です。

/** メインメソッド */
suspend fun main() = Korge(windowSize = Size(512, 512), backgroundColor = Colors["#2b2b2b"]) {
  // メインメソッドの処理
}

この中に処理を書きます。
具体的には、以下のようになります。

  1. main()のなかに「Korge()のウィンドウサイズ512x512で、背景色を「カラーコードの『#2b2b2b』」で表示。
  2. その中に「メインメソッドの処理」を書く

メインメソッドの処理内容としては。下の2行のみ

  1. シーンコンテナ=描画するシーンを入れるオブジェクト=コンテナ(Container)
  2. このコンテナを「MyScene」というクラス(のインスタンス)に変更
val sceneContainer = sceneContainer()

sceneContainer.changeTo { MyScene() }

※コンテナ(MyScene())の内容は割愛します。

そして、動くのは下のようなものです。

TUTORIAL2:図形を描画しよう

図形の描画について
今度は、もともとあったコードを書き換えて下の参考動画のコードを書いて動かしてみようと思います。
参考にするページはこちらです。
参考にする動画は下のものです。

ファイルの内容を変更しようとすると下のようなダイアログが出ますが「OK」して大丈夫です、ファイルの権限を変更して変更可能にする処理を行います。

補足

参考にする動画、ページが実際に使えるもの(書いているコードが動くもの)とそうでないのもがあり、ページが別になってしまうことがあります。
参考ページなどは、リンクを貼っていますので、こちらを参照ください。
Viewなどのクラスに関しては、Viewsのページにありました。

円を描画する

上記の動画で最初に描画する円を描画しました。

作成したプログラムは下のようなコードです。

import korlibs.time.*
import korlibs.korge.*
import korlibs.korge.scene.*
import korlibs.korge.tween.*
import korlibs.korge.view.*
import korlibs.image.color.*
import korlibs.image.format.*
import korlibs.io.file.std.*
import korlibs.math.geom.*
import korlibs.math.interpolation.*
import java.awt.Color

suspend fun main() = Korge(windowSize = Size(512, 512), backgroundColor = Colors["#2b2b2b"]) {
    val circle : ShapeView = circle(20.0, Colors.GOLD).xy(200, 200)

}

これは、単純に円【半径(20.0)、色(金)、位置(座標(200, 200))】を定義してやればプログラムで描画してくれる
高レベルAPIならではのコードです。
このxとかscaleXなどのプロパティは、図形が持っている値のことです。
このページで見ることが出来ます。

円を動かす

上記のコードに下のようなコードを追記してやります。

    circle.addUpdater {
        x++
        scaleX += 0.05
        scaleY += 0.05
    }

これは「circle」に更新処理を追加するという意味合いがあり、下の動画のように動きます。

図形と画像

参考動画はこちらです。

使用しているクラスが微妙に違うのが難点です。ここら辺の代替えクラスを探す必要があります。。。

ライブラリの登録など。。。

今までやってきたのは「korlibs.*」のライブラリを使用したもので、設定が参照しているサイトと違うことに気が付きました。。。

つまり、プロジェクトを作成するときに下のように指定のプロジェクトを開いてやる必要がありました。

細かいところは全て、裏側でやってくれるのが高レベルAPIです。手順が違うとドハマりするんですね。。。

続きます。。。

Linux(Ubuntsu)でDTMを始める手順

イントロダクション

最近、ノートパソコンのスペックが高くなって、気がついたら、自分のデスクトップPCよりもノートのほうがスペックが高くなり。。。
デスクっトプPCをUbutsuにしたところです。折角LinuxにしたのだからDTM用のPCにすることにしました。

Libux用LMMSの設定へ
でも、LMMSはLinuxでもWindowsでも動かせるということです。


Ubuntsuの使い方

兎にも角にも、まずはUbuntsuの使い方がわからないとDTMもできません。
なので、自分の躓いたところなど記載したいと思います。

1.Ubuntsuの更新

普段(Windows)は、自動で更新がかかりますが、Linux(Ubuntsu)は自己責任なので、自分で更新します。
しかし、更新すると言っても下のようなコマンドで更新する方法とGUI(画面)操作で実行する方法があります。

  1. '''sudo apt upgrade'''
  2. アプリケーションの「ソフトウェアの更新」を使用する

「1」で実行したときにエラーが出たときでも、GUIで実行したら更新できました。
エラーメッセージは、コピーし忘れたので、ビミョウに違うかもですが、下のようなエラーメッセージでした。

ファイルがロックされているため更新できません。

2. 画像ファイルの編集

単純に、キャプチャを撮ったあとにちょいと編集したい場合。例えば赤線で囲むとかしたい場合。
Windowsであれば「ペイントツール」があるのですがUbuntsuは自分でインストールします。
当然無料です。Linuxは基本的にオープンソースでやっているのでお金がかかりません。「その代わり自分で責任持ってね!」というものです。

そんなわけで筆者がインストールしたのは、「Pinta」というツール(アプリケーション)です。

使い方は、こちらのフォーラムなど参考にしました。

3.アプリ一覧のショートカット

筆者はよく使うので。。。
Windowsキーボートであれば、下のコマンドで開けます。

Windowsボタン+A

こちらのサイトには別な書き方をしていました。Widnwsボタンが「Super」になってました。

全般の設定を行うのであれば「設定」アプリを開き、それぞれの設定を行います。


DTMの環境構築(Ubuntsu用)

Ubuntsuの画面がこんな感じです。

まずは音を出すための環境セットアップ

Linuxなので、はじめからインストールされているOSにデフォルトでセットされているアプリケーションをインストールすれば問題ないのでしょうが、スペック的な問題で必要最低限でインストールしたため
追加でインストールするものがあります。

スピーカーの設定と音確認

まずは下のアプリをインストールします。インストール済みの場合は飛ばして良いです。
ちなみに、インストールできていない場合は、設定アプリを開いたときに「サウンド」がありません。

PulxAudioをインストール

下のコマンドを叩きます。そして、コントローラも別個にインストールが必要です。

sudo apt install pulseaudio

PulseAudio Volume Controlをインストール

sudo apt install pavucontrol

これらがインストールできたら設定を行います。

スピーカーのセット

サウンドの設定画面で使用するデバイス(スピーカー)を選択して、テストして音が出ることを確認します。

LMMSの使い方を学ぶ

こちらのサイトでLMMSの使い方 を学習します。

LMMSのダウンロード

こちらのURLよりダウンロードサイトに移動できます。ダウンロードすると下のようなファイルができます。

AppImageファイルの展開

他のサイトを見るとダブルクリックでインストールできるようですが、自分のところはうまく行きませんでした。

なので下のように行いました。

  1. ダウンロードフォルダにAppImageをダウンロードする。

  2. ターミナルを開き以下を実行

    ./lmms-1.2.2-linux-x86_64.AppImage --appimage-extract
  3. 作成されたフォルダへ移動して「./AppRun」を実行

とりあえずはこんな感じです。

Windows用LMMSの設定

Windowsは簡単です。インストーラーをダウンロードして、インストールウィザードでサクッと行けます。

  1. ダウンロードページを開くLMMSの日本語サイトへ行きます

  2. インストーラーを起動する

  3. ウィンドウを進める

  4. インストール場所を指定する。初期設定は「C:\ProgramFiles」になる

  5. 最後にショートカットを作成するかを指定してインストール

  6. 今回は、ショートカットを作成しなかったので直接ショートカットを作成「C:\ProgramFiles」を開いて右クリック

  7. 実効時に初期設定の画面が表示されるが後で再設定可能

  8. 初期画面の表示

とりあえずはここまでです。

OracleDBの環境構築

イントロダクション

オラクルのデータベース(アプリケーション)を使用するのに無償で扱うことができます。細かいところはこちらに記述しました。

色々と調べてみるとOracleEnterprizeManagerがとても便利なのでそちらを使うと良いということがわかりました。

そして下の動画で学習しました。

OracleDB環境構築

まずは、コマンド名ですべてをやるととても面倒だということがあります。かといってGUIのみでやるのも大変。。。

早い話が、敵視ツールを使用するのがベストということでした。
次の作業は、SqlPlusを使用して行いました。

作成したDBの確認

インストールが終わると、初期状態で作成されるDBがあります。それが下のようなものになります。

select name from v$pdbs;

DBへ接続する

ここでいう「DB」とはPDB(プラガブルデータベース)のことです。

ユーザー作成

下のSQLを実行します。「create user XXX identified by パスワード」という形の文章です。そのあとの文章は、テーブルスペースなどのメモリ使用量とか、権限の範囲などを指定しています。細かいところはわからないので上記のチュートリアルに従います。

create user demouser identified by demouser default tablespace users quota unlimited on users;

改めてログインするのには、
下のようにコマンドを入力

connect demouser/demouser@localhost:1521/XEPDB1 as sysdba

しかし、「as sysdba」をつけないとログインができませんでした。とエラーが出ました。

Listener refused the connection with the flloing error ORA-12514 ...

調べてみるとリスナーの状態を確認するのが初手のようです。

コマンドプロンプトを開き「lsnrctl status」で確認...

色々やったけど、「sys」ユーザーでログインするのが手っ取り早いと思い下のようにログインしました。

次は、作成したユーザー(スキーマ)でログインしたいと思います。

JavaFX + SceneBuilderでアプリケーションを作る

イントロダクション

受付用のアプリケーションを作成したいと思いました。イメージは次のような形です。

  1. 自己紹介カードなどを作成して、これにバーコードを付ける。
  2. PCでアプリケーションを作成(JavaFXなどのGUI)して、入力を受ける
  3. REST通信でユーザーの情報を受け取り画面に表示する
  4. 文字入力を受け付けて、サーバー上に登録、管理する

JavaFXとScceneBuilderでGUI作成

IntelliJ IDEAを使用して作業を行います。まずはこちらからIntelliJをダウンロード and インストールを行います。

プロジェクトの作成

新規でプロジェクトを作成します。New Projectを選択して「JavaFX」を選択します。

そして、どのタイプを作成するか選択します。細かい説明は英語ですが書いてあるので、日本語版にすれば日本語で見れるかもしれません。

アプリケーションを作る

プロジェクトを作成したら、基本的なコードが自動生成されます。
それを眺めてみましょう。

自動生成されたコード

まずは、生成されたファイルを確認します。下のようなファイルが生成されました。

  • HelloApplication.java
  • HelloController
  • module-info.java
  • hello-view.fxml

このうち「module-info.java」は気にしなくてよいファイルになります。

HelloApplication.java

まずはメインメソッドのあるこのクラスを眺めてみましょう。ポイントは「Application」クラスを継承しているところです。
このクラスを継承して、「start()」メソッドをオーバーライドしています。
このメソッドをオーバーライドすることで画面を作成するのに必要な処理を大体やってくれます。

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

そして、「start()」メソッドで、自分が作りたい画面を表示するために、ペインを追加したり、ボタンを生成したり、ラベルで文字を作ったりするわけです。下のようにコードでボタンをペインに追加します。

Button helloButton = new Button("Hello JavaFX");
pain.add(helloButton);

しかし、SceneBuilderが登場してからは、GUIでこのような部分を作成することができるようになりました。

こんなかんじでRXMLファイルを作成します。

FXML

生成されたファイルの中に、FXMLもあります。

このファイルは、SceneBuilderで開くと下のように見えます。

画面の下の方にボタンがあるだけです。
しかしこれを実行すると下のように動きます。

ちなみにXMLファイルなので、下のような内容が記述されています。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="jp.zenryoku.doms.xxx.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Label fx:id="welcomeText"/>
    <Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

Controller

これは、上記のFXMLファイルの中に記述されている下の部分に着目してください。

fx:controller="jp.zenryoku.doms.xxx.HelloController">

XMLファイルで「HelloController」を参照するように記述があります。

そして、プログラムコードは下のようなものです。「@FXML」がポイントです。
このアノテーションを使用してラベルにアクセスし、どのようなアクションを起こすか定義します。

public class HelloController {
    @FXML
    private Label welcomeText;

    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }
}

アクセスするコンポーネント

ここでいう「コンポーネント」はラベルのことです。「コンポーネント」は幅の広い言葉です。
「日本人」というと日本全国を含みますが、「北海道人」というと北海道の人のみです。

同じように「コンポーネント」という場合は、JavaFXで使用するクラス全体のことを言います。主に画面コンポーネントを示すことが多いです。
「画面コンポーネント」というのはラベル、ボタン、ペイン、パネル、テキストエリア...のことです。HTMLみたいですよね。

実際にHTMLふぃあるから画面を生成することもできます。かなり優秀です(笑)

さて、下の行に注目してください。

@FXML
private Label welcomeText;

コントローラーで上のようにフィールド変数が定義されています。これは、FXMLにも同様の定義があります。

<Label fx:id="welcomeText"/>

"fx:id" 属性で使用するコンポーネントをクラスとFXMLを関連付けています。つまりは、「welcomeText」という変数名のラベルをコントローラーのメソッドで下のように処理します。ということを定義しています。

@FXML
protected void onHelloButtonClick() {
    welcomeText.setText("Welcome to JavaFX Application!");
}

Button

結局は、ボタンを押下したときに上の文字列が画面に表示されるのですが、ここの「アクション」を定義しているのが次の部分です。

  1. ボタンを押下したときの処理、「onAction」で定義している

    <Button text="Hello!" onAction="#onHelloButtonClick"/>
  2. onHelloButtonClickはコントローラーに定義している

    @FXML
    protected void onHelloButtonClick() {
    welcomeText.setText("Welcome to JavaFX Application!");
    }

上記のような形で実装されています。ここまでくればあとは各コンポーネントとそれぞれのアクションを定義してやれば、オリジナルのアプリケーションを作成できます。

でわでわ。。。